Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2213 lines
54 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. vtutf8scraper.cpp
  5. Abstract:
  6. Class for performing vtutf8 screen scrapning of a command console shell
  7. Author:
  8. Brian Guarraci (briangu) 2001.
  9. Revision History:
  10. (remotely based on scraper.cpp from telnet code)
  11. --*/
  12. #include <cmnhdr.h>
  13. #include <Scraper.h>
  14. #include <utils.h>
  15. #include "vtutf8scraper.h"
  16. //
  17. // Termcap globals
  18. //
  19. #define CM_STRING_LENGTH 9
  20. LPWSTR lpszCMResultsBuffer = NULL;
  21. //the string is of the form <ESC>[ Ps m
  22. #define SGR_STRING_LENGTH 6
  23. PWCHAR szSGRStr = NULL;
  24. #define SGR( szSGRString, num ) \
  25. wnsprintf( \
  26. szSGRString, \
  27. sizeof(WCHAR) * SGR_STRING_LENGTH, \
  28. L"\033[%dm", \
  29. num \
  30. ); \
  31. szSGRString[SGR_STRING_LENGTH-1] = UNICODE_NULL;
  32. //
  33. // Number of seconds before we timeout waiting for
  34. // the next character in the
  35. // <esc>ctrl-a
  36. // <esc>ctrl-c
  37. // <esc>ctrl-u
  38. //
  39. #define ESC_CTRL_SEQUENCE_TIMEOUT (2 * 1000)
  40. //
  41. // Tick Marker for when we get an esc-ctrl-X sequence
  42. //
  43. DWORD TimedEscSequenceTickCount = 0;
  44. //
  45. // Globals for tracking the screen scraping attributes
  46. //
  47. COORD coExpectedCursor = { ~0, ~0 };
  48. BOOL fBold = false;
  49. WORD wExistingAttributes = 0;
  50. WORD wDefaultAttributes = 0;
  51. //
  52. //
  53. //
  54. #define MAX_SIZEOF_SCREEN (m_wMaxRows * m_wMaxCols * sizeof( CHAR_INFO ))
  55. //
  56. //
  57. //
  58. #define DBG_DUMP_SCREEN_INFO()\
  59. { \
  60. WCHAR blob[256]; \
  61. wsprintf(blob,L"wRows: %d\n", m_wRows); \
  62. OutputDebugString(blob); \
  63. wsprintf(blob,L"wCols: %d\n", m_wCols); \
  64. OutputDebugString(blob); \
  65. wsprintf(blob,L"r*c: %d\n", m_wRows * m_wCols); \
  66. OutputDebugString(blob); \
  67. wsprintf(blob,L"sizeof: %d\n", sizeof(CHAR_INFO)); \
  68. OutputDebugString(blob); \
  69. wsprintf(blob,L"r*c*s: %d\n", m_wRows * m_wCols * sizeof(CHAR_INFO)); \
  70. OutputDebugString(blob); \
  71. wsprintf(blob,L"max: %d\n", MAX_SIZEOF_SCREEN); \
  72. OutputDebugString(blob); \
  73. }
  74. CVTUTF8Scraper::CVTUTF8Scraper()
  75. /*++
  76. Routine Description:
  77. Default constructor - this should not be used.
  78. Arguments:
  79. None
  80. Return Value:
  81. N/A
  82. --*/
  83. {
  84. pLastSeen = NULL;
  85. pCurrent = NULL;
  86. m_hConBufIn = NULL;
  87. m_hConBufOut = NULL;
  88. m_dwInputSequenceState = IP_INIT;
  89. m_dwDigitInTheSeq = 0;
  90. m_readBuffer = NULL;
  91. lpszCMResultsBuffer = NULL;
  92. szSGRStr = NULL;
  93. }
  94. CVTUTF8Scraper::CVTUTF8Scraper(
  95. CIoHandler *IoHandler,
  96. WORD wCols,
  97. WORD wRows
  98. ) : CScraper(
  99. IoHandler,
  100. wCols,
  101. wRows
  102. )
  103. /*++
  104. Routine Description:
  105. Constructor - parameterized
  106. Arguments:
  107. IoHandler - IoHandler the scraper should use
  108. wCols - the # of rows the console screen buffer should have
  109. wRows - the # of cols the console screen buffer should have
  110. Return Value:
  111. N/A
  112. --*/
  113. {
  114. pLastSeen = NULL;
  115. pCurrent = NULL;
  116. m_dwInputSequenceState = IP_INIT;
  117. m_dwDigitInTheSeq = 0;
  118. m_readBuffer = new WCHAR[READ_BUFFER_LENGTH];
  119. lpszCMResultsBuffer = new WCHAR[CM_STRING_LENGTH];
  120. szSGRStr = new WCHAR[SGR_STRING_LENGTH];
  121. }
  122. CVTUTF8Scraper::~CVTUTF8Scraper()
  123. /*++
  124. Routine Description:
  125. Destructor
  126. Arguments:
  127. N/A
  128. Return Value:
  129. N/A
  130. --*/
  131. {
  132. //
  133. // release our screen buffers
  134. //
  135. if( pLastSeen )
  136. {
  137. delete[] pLastSeen;
  138. }
  139. if( pCurrent )
  140. {
  141. delete[] pCurrent;
  142. }
  143. if (m_readBuffer) {
  144. delete[] m_readBuffer;
  145. }
  146. if (lpszCMResultsBuffer) {
  147. delete[] lpszCMResultsBuffer;
  148. }
  149. if (szSGRStr) {
  150. delete[] szSGRStr;
  151. }
  152. //
  153. // Parent CScraper closes Con I/O handles
  154. //
  155. NOTHING;
  156. }
  157. void
  158. CVTUTF8Scraper::ResetLastScreen(
  159. VOID
  160. )
  161. /*++
  162. Routine Description:
  163. Reset the memory of the last display
  164. This forces the screen scraper to think that everything
  165. is different, hence it sends a full screen dump
  166. Arguments:
  167. None
  168. Return Value:
  169. None
  170. --*/
  171. {
  172. //
  173. // clear the screen buffers and screen info
  174. //
  175. memset( &LastCSBI, 0, sizeof( LastCSBI ) );
  176. memset( pLastSeen, 0, MAX_SIZEOF_SCREEN );
  177. memset( pCurrent, 0, MAX_SIZEOF_SCREEN );
  178. fBold = false;
  179. wExistingAttributes = 0;
  180. wDefaultAttributes = 0;
  181. }
  182. BOOL
  183. CVTUTF8Scraper::DisplayFullScreen(
  184. VOID
  185. )
  186. /*++
  187. Routine Description:
  188. This routine forces the screen scraper to think that everything
  189. is different, hence it sends a full screen dump
  190. Arguments:
  191. None
  192. Return Value:
  193. TRUE - success
  194. FALSE - otherwise
  195. --*/
  196. {
  197. BOOL bSuccess;
  198. //
  199. // Reset the memory of the last display
  200. //
  201. // This forces the screen scraper to think that everything
  202. // is different, hence it sends a full screen dump
  203. //
  204. ResetLastScreen();
  205. //
  206. // Call the screen scraper
  207. //
  208. bSuccess = Write();
  209. return bSuccess;
  210. }
  211. BOOL
  212. CVTUTF8Scraper::SetScreenBufferInfo(
  213. VOID
  214. )
  215. /*++
  216. Routine Description:
  217. This routine sets the current console screen buffer's
  218. parameters to what we think they should be. The primary
  219. purpose for this routine is to provide a means to change
  220. the screen buffer max X/Y to the m_wCols/m_wRows so that
  221. the output fits in our scraping window.
  222. Arguments:
  223. pCSBI - the current console screen buffer info
  224. Return Value:
  225. TRUE - success
  226. FALSE - otherwise
  227. --*/
  228. {
  229. COORD coordLargest;
  230. COORD coordSize;
  231. //
  232. // Start with our max window size and shrink if we need to
  233. //
  234. m_wCols = m_wMaxCols;
  235. m_wRows = m_wMaxRows;
  236. //
  237. // Get the current window info
  238. //
  239. coordLargest = GetLargestConsoleWindowSize( m_hConBufOut );
  240. if( coordLargest.X < m_wCols && coordLargest.X != 0 )
  241. {
  242. m_wCols = coordLargest.X;
  243. }
  244. if( coordLargest.Y < m_wRows && coordLargest.Y != 0 )
  245. {
  246. m_wRows = coordLargest.Y;
  247. }
  248. //
  249. // make the window the size we think it should be
  250. //
  251. coordSize.X = m_wCols;
  252. coordSize.Y = m_wRows;
  253. SetConsoleScreenBufferSize( CVTUTF8Scraper::m_hConBufOut, coordSize );
  254. return( TRUE );
  255. }
  256. BOOL
  257. CVTUTF8Scraper::SetWindowInfo(
  258. VOID
  259. )
  260. /*++
  261. Routine Description:
  262. This routine sets the initial console window info.
  263. Arguments:
  264. None
  265. Return Value:
  266. TRUE - success
  267. FALSE - otherwise
  268. --*/
  269. {
  270. COORD coordLargest;
  271. SMALL_RECT sr;
  272. COORD coordSize;
  273. CONSOLE_SCREEN_BUFFER_INFO csbi;
  274. //
  275. // Start with our max window size and shrink if we need to
  276. //
  277. m_wCols = m_wMaxCols;
  278. m_wRows = m_wMaxRows;
  279. //
  280. // Get the current window info
  281. //
  282. coordLargest = GetLargestConsoleWindowSize( m_hConBufOut );
  283. if( coordLargest.X < m_wCols && coordLargest.X != 0 )
  284. {
  285. m_wCols = coordLargest.X;
  286. }
  287. if( coordLargest.Y < m_wRows && coordLargest.Y != 0 )
  288. {
  289. m_wRows = coordLargest.Y;
  290. }
  291. //
  292. //
  293. //
  294. sr.Top = 0;
  295. sr.Bottom = ( WORD )( m_wRows - 1 );
  296. sr.Right = ( WORD ) ( m_wCols - 1 );
  297. sr.Left = 0;
  298. //
  299. //
  300. //
  301. coordSize.X = m_wCols;
  302. coordSize.Y = m_wRows;
  303. //
  304. //
  305. //
  306. GetConsoleScreenBufferInfo( CVTUTF8Scraper::m_hConBufOut, &csbi);
  307. // Logic: If the Old Window Size is less than the new Size then we set
  308. // the Screen Buffer Size first and then set the Window Size.
  309. // If the Old Window Size is Greater than the new Size then we set the
  310. // window Size first and then the Screen Buffer.
  311. // The above is because the Buffer Size always has to be greater than or
  312. // equal to the Window Size.
  313. if ( (csbi.dwSize.X < coordSize.X) || (csbi.dwSize.Y < coordSize.Y) )
  314. {
  315. COORD coordTmpSize = { 0, 0 };
  316. coordTmpSize .X = ( csbi.dwSize.X < coordSize.X ) ? coordSize.X : csbi.dwSize.X;
  317. coordTmpSize .Y = ( csbi.dwSize.Y < coordSize.Y ) ? coordSize.Y : csbi.dwSize.Y;
  318. SetConsoleScreenBufferSize ( CVTUTF8Scraper::m_hConBufOut, coordTmpSize );
  319. SetConsoleWindowInfo ( CVTUTF8Scraper::m_hConBufOut, TRUE, &sr );
  320. SetConsoleScreenBufferSize ( CVTUTF8Scraper::m_hConBufOut, coordSize );
  321. }
  322. else
  323. {
  324. SetConsoleWindowInfo( CVTUTF8Scraper::m_hConBufOut, TRUE, &sr );
  325. SetConsoleScreenBufferSize( CVTUTF8Scraper::m_hConBufOut, coordSize );
  326. }
  327. return( TRUE );
  328. }
  329. BOOL
  330. CVTUTF8Scraper::InitScraper(
  331. VOID
  332. )
  333. /*++
  334. Routine Description:
  335. This routine initializes the local scraper configures the console
  336. to be the dimensions that the scraper requires.
  337. Arguments:
  338. None
  339. Return Value:
  340. TRUE - the scraper was initialized
  341. FALSE - otherwise
  342. --*/
  343. {
  344. //
  345. // Configure the console dimensions
  346. //
  347. if( !SetWindowInfo() )
  348. {
  349. return( FALSE );
  350. }
  351. //
  352. // Create and initialize the scraper buffers
  353. //
  354. if( pLastSeen )
  355. {
  356. delete[] pLastSeen;
  357. }
  358. if( pCurrent )
  359. {
  360. delete[] pCurrent;
  361. }
  362. pLastSeen = ( PCHAR_INFO ) new char[MAX_SIZEOF_SCREEN];
  363. pCurrent = ( PCHAR_INFO ) new char[MAX_SIZEOF_SCREEN];
  364. ASSERT(pLastSeen);
  365. ASSERT(pCurrent);
  366. if( !pLastSeen || !pCurrent )
  367. {
  368. return ( FALSE );
  369. }
  370. //
  371. // Initialize the screen buffers and info
  372. //
  373. memset( &LastCSBI, 0, sizeof( LastCSBI ) );
  374. memset( pCurrent, 0, MAX_SIZEOF_SCREEN );
  375. memset( pLastSeen, 0, MAX_SIZEOF_SCREEN );
  376. return( TRUE );
  377. }
  378. BOOL
  379. CVTUTF8Scraper::CreateIOHandle(
  380. IN PWCHAR HandleName,
  381. OUT PHANDLE pHandle
  382. )
  383. /*++
  384. Routine Description:
  385. This routine opens a handle of the specified name.
  386. Note: This routine used to open CONIN$ and CONOUT$ handles
  387. for the console being scraped.
  388. Arguments:
  389. HandleName - the name of the handle to open
  390. pHandle - on success, the resulting handle
  391. Return Value:
  392. TRUE - the handle was created
  393. FALSE - otherwise
  394. --*/
  395. {
  396. BOOL bSuccess;
  397. //
  398. // default: failed to open handle
  399. //
  400. bSuccess = FALSE;
  401. do {
  402. //
  403. // Attempt to open the console input handle
  404. //
  405. *pHandle = CreateFile(
  406. HandleName,
  407. GENERIC_READ | GENERIC_WRITE,
  408. 0,
  409. NULL,
  410. OPEN_EXISTING,
  411. FILE_ATTRIBUTE_NORMAL,
  412. NULL
  413. );
  414. ASSERT( *pHandle != INVALID_HANDLE_VALUE );
  415. if ( *pHandle == INVALID_HANDLE_VALUE) {
  416. break;
  417. }
  418. //
  419. // We were successful
  420. //
  421. bSuccess = TRUE;
  422. } while ( FALSE );
  423. return bSuccess;
  424. }
  425. BOOL
  426. CVTUTF8Scraper::CreateConsoleOutHandle(
  427. VOID
  428. )
  429. /*++
  430. Routine Description:
  431. This routine creates a CONOUT$ handle to the current console screen buffer.
  432. Arguments:
  433. None
  434. Return Value:
  435. TRUE - the handle was created
  436. FALSE - otherwise
  437. --*/
  438. {
  439. BOOL bSuccess;
  440. HANDLE h;
  441. //
  442. // Close the current console out handle
  443. //
  444. if ((m_hConBufOut != NULL) && (m_hConBufOut != INVALID_HANDLE_VALUE)) {
  445. CloseHandle(m_hConBufOut);
  446. }
  447. //
  448. // Create the new console out handle
  449. //
  450. bSuccess = CreateIOHandle(
  451. L"CONOUT$",
  452. &h
  453. );
  454. if (bSuccess) {
  455. SetConOut(h);
  456. }
  457. return bSuccess;
  458. }
  459. BOOL
  460. CVTUTF8Scraper::CreateConsoleInHandle(
  461. VOID
  462. )
  463. /*++
  464. Routine Description:
  465. This routine creates a CONIN$ handle to the current console screen buffer.
  466. Arguments:
  467. None
  468. Return Value:
  469. TRUE - the handle was created
  470. FALSE - otherwise
  471. --*/
  472. {
  473. BOOL bSuccess;
  474. HANDLE h;
  475. //
  476. // Close the current console in handle
  477. //
  478. if ((m_hConBufIn != NULL) && (m_hConBufIn != INVALID_HANDLE_VALUE)) {
  479. CloseHandle(m_hConBufIn);
  480. }
  481. //
  482. // Create the new console out handle
  483. //
  484. bSuccess = CreateIOHandle(
  485. L"CONIN$",
  486. &h
  487. );
  488. if (bSuccess) {
  489. SetConIn(h);
  490. }
  491. return bSuccess;
  492. }
  493. BOOL
  494. CVTUTF8Scraper::CreateIOHandles(
  495. VOID
  496. )
  497. /*++
  498. Routine Description:
  499. This routine creates CONIN$ and CONOUT$ handles to the
  500. current console screen buffer.
  501. Arguments:
  502. None
  503. Return Value:
  504. TRUE - the handles were created
  505. FALSE - otherwise
  506. --*/
  507. {
  508. //
  509. // note: the scraper parent class will reap the std io handles
  510. //
  511. return ((CreateConsoleOutHandle()) && (CreateConsoleInHandle()));
  512. }
  513. BOOL
  514. CVTUTF8Scraper::Start(
  515. VOID
  516. )
  517. /*++
  518. Routine Description:
  519. This routine initializes the scraper and prepares it so
  520. that this routine can be immediately followed by a Write().
  521. Arguments:
  522. None
  523. Return Value:
  524. TRUE - the scraper was started
  525. FALSE - otherwise
  526. --*/
  527. {
  528. BOOL bSuccess;
  529. //
  530. // Create the Console IO Handles
  531. //
  532. bSuccess = CreateIOHandles();
  533. ASSERT_STATUS(bSuccess, FALSE);
  534. //
  535. // Initialize the scraper
  536. //
  537. bSuccess = InitScraper();
  538. ASSERT_STATUS(bSuccess, FALSE);
  539. //
  540. // Reset the last screen display memory
  541. //
  542. ResetLastScreen();
  543. return( TRUE );
  544. }
  545. BOOL
  546. CVTUTF8Scraper::Write(
  547. VOID
  548. )
  549. /*++
  550. Routine Description:
  551. This routine scrapes the current console buffer and writes the emulated
  552. terminal output to the current IoHandler.
  553. Note: In order to guarantee coherency between what the scaper sees and
  554. the apps being scraped, this routine ensures the current screen
  555. buffer is of the expected dimensions the scraper cares about.
  556. If the dimensions are not correct, it forces them to what it expects.
  557. For instance, Edit resizes the window to 80x25, but the scraper may
  558. expect 80x24, so the console buffer gets resized to 80x24.
  559. Arguments:
  560. None
  561. Return Value:
  562. TRUE - the write operation was successful
  563. FALSE - otherwise
  564. Security:
  565. Interface:
  566. Console - we write user input to the console
  567. --*/
  568. {
  569. BOOL bSuccess;
  570. DWORD dwStatus;
  571. //
  572. // Open a handle to the active console screen buffer
  573. //
  574. // Note: This is a necessary step.
  575. // We need to ensure we have a handle to the
  576. // current console screen buffer before we
  577. // attempt to screen scrape.
  578. // The reason we concern ourselves with having
  579. // the current screen buffer handle is because
  580. // apps can use the CreateConsoleScreenBuffer &
  581. // SetConsoleActiveScreenBuffer APIs. These
  582. // APIs effectively invalidate our CONOUT$ until
  583. // they switch back to their original conout handle.
  584. // A typical use scenario is:
  585. //
  586. // 1. get original conout handle:
  587. // PrevConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  588. // 2. create a new console screen buffer
  589. // 3. set new screen buffer as the active one
  590. // 4. do work using new screen buffer...
  591. // 5. set screen buffer to original
  592. //
  593. if ( !CreateConsoleOutHandle() ) {
  594. //Could be an application that does not share its screen buffer
  595. return TRUE;
  596. }
  597. //
  598. // Read the current screen buffer info
  599. //
  600. dwStatus = GetConsoleScreenBufferInfo( m_hConBufOut, &CSBI );
  601. ASSERT( dwStatus );
  602. if( !dwStatus )
  603. {
  604. return ( FALSE );
  605. }
  606. //
  607. // Make sure the screen buffer size hasn't changed.
  608. // if it has, then set it back.
  609. //
  610. if (CSBI.dwMaximumWindowSize.X > m_wCols ||
  611. CSBI.dwMaximumWindowSize.Y > m_wRows
  612. ) {
  613. //
  614. // We have detected a change in the screen buffer settings
  615. // this very likely means that an app has created a new
  616. // screen buffer and made it the active one. In order
  617. // to not confuse the app during its initialization phase
  618. // we need to pause for a short period before resetting
  619. // the screen buffer parameters.
  620. //
  621. Sleep(100);
  622. //
  623. //
  624. //
  625. if( !SetScreenBufferInfo() ) {
  626. return( FALSE );
  627. }
  628. //
  629. // reread the current screen buffer info
  630. //
  631. dwStatus = GetConsoleScreenBufferInfo( m_hConBufOut, &CSBI );
  632. ASSERT( dwStatus );
  633. if( !dwStatus )
  634. {
  635. return ( FALSE );
  636. }
  637. }
  638. //
  639. // Perform the screen scraping
  640. //
  641. bSuccess = CompareAndUpdate(
  642. m_wRows,
  643. m_wCols,
  644. pCurrent,
  645. pLastSeen,
  646. &CSBI,
  647. &LastCSBI
  648. );
  649. return bSuccess;
  650. }
  651. BOOL
  652. CVTUTF8Scraper::Read()
  653. /*++
  654. Routine Description:
  655. this routine retrieves user-input from the IoHandler and
  656. sends it to the current console buffer.
  657. Arguments:
  658. None
  659. Return Value:
  660. TRUE - the read operation was successful
  661. FALSE - otherwise
  662. Security:
  663. Inteface:
  664. external input --> internal
  665. --*/
  666. {
  667. DWORD i;
  668. BOOL dwStatus = 0;
  669. DWORD dwCharsTransferred = 0;
  670. ULONG bufferSize;
  671. BOOL bInEnhancedCharSequence;
  672. BOOL bSuccess;
  673. //
  674. // read Channel::stdin
  675. //
  676. bSuccess = m_IoHandler->Read(
  677. (PUCHAR)m_readBuffer,
  678. READ_BUFFER_SIZE,
  679. &bufferSize
  680. );
  681. if (!bSuccess) {
  682. return (FALSE);
  683. }
  684. //
  685. // Determine the # of WCHARs read
  686. //
  687. dwCharsTransferred = bufferSize / sizeof(WCHAR);
  688. //
  689. // Process the read characters
  690. //
  691. for( i=0; i < dwCharsTransferred; i++ ) {
  692. //
  693. // Examine the input stream and parse out any VT-UTF8 escape sequences
  694. // that are present.
  695. //
  696. bInEnhancedCharSequence = ProcessEnhancedKeys( m_readBuffer[i] );
  697. //
  698. // If the last character processed started/continued an
  699. // enhanced key sequence, then continue processing enhanced
  700. // keys
  701. //
  702. if (bInEnhancedCharSequence) {
  703. continue;
  704. }
  705. //
  706. // Handle ctrl-c (this behavior is taken from tlntsess.exe)
  707. //
  708. if( (UCHAR)m_readBuffer[i] == CTRLC ) {
  709. //
  710. //The follwing is the observed behaviour of CTRL C
  711. //When ENABLE_PROCESSED_INPUT mode is not enabled, pass CTRL C as
  712. //input to the console input buffer.
  713. //When ENABLE_PROCESSED_INPUT mode is enabled, generate CTTRL C signal
  714. //and also unblock any ReadConsoleInput. This behaviour is what is observed
  715. // and not from any documentation.
  716. //
  717. DWORD dwMode = 0;
  718. GetConsoleMode( m_hConBufIn, &dwMode );
  719. if( dwMode & ENABLE_PROCESSED_INPUT ) {
  720. GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0);
  721. continue;
  722. }
  723. }
  724. //
  725. // Send the character to the command console
  726. //
  727. dwStatus = WriteAKeyToCMD(m_readBuffer[i]);
  728. if( !dwStatus ) {
  729. return ( FALSE );
  730. }
  731. }
  732. return( TRUE );
  733. }
  734. DWORD
  735. CVTUTF8Scraper::WriteAKeyToCMD(
  736. WCHAR Char
  737. )
  738. /*++
  739. Routine Description:
  740. This routine sends a character to the console.
  741. Arguments:
  742. Char - the character to send
  743. Return Value:
  744. status
  745. --*/
  746. {
  747. DWORD dwStatus;
  748. SHORT vk;
  749. BYTE vkcode;
  750. WORD wScanCode;
  751. //
  752. // default: failure
  753. //
  754. dwStatus = 0;
  755. //
  756. // Get the virtual key scan code for the character
  757. //
  758. vk = VkKeyScan( (UCHAR)Char );
  759. if( vk != 0xffff )
  760. {
  761. DWORD dwShiftcode;
  762. //
  763. // translates (maps) a virtual-key code into a scan code
  764. //
  765. vkcode = LOBYTE( vk );
  766. wScanCode = ( WORD )MapVirtualKey( vkcode, 0 );
  767. //
  768. // determine if any modifiers need to be sent
  769. //
  770. dwShiftcode = 0;
  771. if( HIBYTE( vk ) & 1 ) {
  772. dwShiftcode |= SHIFT_PRESSED;
  773. }
  774. if( HIBYTE( vk ) & 2 ) {
  775. dwShiftcode |= LEFT_CTRL_PRESSED;
  776. }
  777. if( HIBYTE( vk ) & 4 ) {
  778. dwShiftcode |= LEFT_ALT_PRESSED;
  779. }
  780. //
  781. // Send the character and modifiers
  782. //
  783. dwStatus = WriteAKeyToCMD(
  784. TRUE,
  785. vkcode,
  786. wScanCode,
  787. Char,
  788. dwShiftcode
  789. );
  790. }
  791. return dwStatus;
  792. }
  793. DWORD
  794. CVTUTF8Scraper::WriteAKeyToCMD(
  795. WORD wVKCode,
  796. WORD wVSCode,
  797. WCHAR Char,
  798. DWORD dwCKState
  799. )
  800. /*++
  801. Routine Description:
  802. This routine sends a character which has already been scanned
  803. to the console.
  804. Arguments:
  805. wVKCode - the virtual key code
  806. wVSCode - the scan code
  807. Char - the character to send
  808. dwCKState - the control key state
  809. Return Value:
  810. --*/
  811. {
  812. return WriteAKeyToCMD(
  813. TRUE,
  814. wVKCode,
  815. wVSCode,
  816. Char,
  817. dwCKState
  818. );
  819. }
  820. DWORD
  821. CVTUTF8Scraper::WriteAKeyToCMD(
  822. BOOL bKeyDown,
  823. WORD wVKCode,
  824. WORD wVSCode,
  825. WCHAR Char,
  826. DWORD dwCKState
  827. )
  828. /*++
  829. Routine Description:
  830. This routine sends a character which has already been scanned
  831. to the console. In addition, the caller can specify the key
  832. press status of this character.
  833. Arguments:
  834. bKeyDown - the key status of the character
  835. wVKCode - the virtual key code
  836. wVSCode - the scan code
  837. Char - the character to send
  838. dwCKState - the control key state
  839. Return Value:
  840. Status
  841. Security:
  842. external input --> interal
  843. we write data retrieved from remote user to the
  844. cmd console stdin
  845. --*/
  846. {
  847. DWORD dwStatus = 0;
  848. DWORD dwCount = 0;
  849. INPUT_RECORD input;
  850. ZeroMemory( &input, sizeof( INPUT_RECORD ) );
  851. input.EventType = KEY_EVENT;
  852. input.Event.KeyEvent.bKeyDown = bKeyDown;
  853. input.Event.KeyEvent.wRepeatCount = 1;
  854. input.Event.KeyEvent.wVirtualKeyCode = wVKCode;
  855. input.Event.KeyEvent.wVirtualScanCode = wVSCode;
  856. input.Event.KeyEvent.uChar.UnicodeChar = Char;
  857. input.Event.KeyEvent.dwControlKeyState = dwCKState;
  858. dwStatus = WriteConsoleInput( m_hConBufIn, &input, 1, &dwCount );
  859. return dwStatus;
  860. }
  861. BOOL
  862. CVTUTF8Scraper::IsValidControlSequence(
  863. VOID
  864. )
  865. /*++
  866. Routine Description:
  867. Determine how long it has been since the esc-ctrl-a sequence
  868. Arguments:
  869. None
  870. Return Value:
  871. TRUE - the control sequence has occured with the allowable time-frame
  872. FALSE - otherwise
  873. --*/
  874. {
  875. DWORD DeltaT;
  876. DeltaT = GetAndComputeTickCountDeltaT(TimedEscSequenceTickCount);
  877. return (DeltaT <= ESC_CTRL_SEQUENCE_TIMEOUT);
  878. }
  879. BOOL
  880. CVTUTF8Scraper::ProcessEnhancedKeys(
  881. IN WCHAR cCurrentChar
  882. )
  883. /*++
  884. Routine Description:
  885. This routine parse the character stream and determines if there
  886. have been any enhanced key sequences. If so, it removes the sequence
  887. from the character stream and send the key to the console.
  888. Arguments:
  889. cCurrentChar - the current character in the stream
  890. Return Value:
  891. TRUE - The character processed started/continued an enhanced key sequence
  892. FALSE - otherwise
  893. --*/
  894. {
  895. BOOL bRetVal = true;
  896. switch( m_dwInputSequenceState )
  897. {
  898. case IP_INIT:
  899. switch (cCurrentChar) {
  900. case ESC:
  901. //
  902. // We are now in an <esc> sequence
  903. //
  904. m_dwInputSequenceState = IP_ESC_RCVD;
  905. break;
  906. default:
  907. //
  908. // Not any special char
  909. //
  910. bRetVal = false;
  911. break;
  912. }
  913. break;
  914. case IP_ESC_RCVD:
  915. m_dwInputSequenceState = IP_INIT;
  916. //
  917. // Map the VT-UTF8 encodings of the ENHANCED keys
  918. //
  919. switch (cCurrentChar) {
  920. case '[':
  921. m_dwInputSequenceState = IP_ESC_BRACKET_RCVD;
  922. break;
  923. case 'h':
  924. WriteAKeyToCMD( VK_HOME, VS_HOME, 0, ENHANCED_KEY );
  925. break;
  926. case 'k':
  927. WriteAKeyToCMD( VK_END, VS_END, 0, ENHANCED_KEY );
  928. break;
  929. case '+':
  930. WriteAKeyToCMD( VK_INSERT, VS_INSERT, 0, ENHANCED_KEY );
  931. break;
  932. case '-':
  933. WriteAKeyToCMD( VK_DELETE, VS_DELETE, 0, ENHANCED_KEY );
  934. break;
  935. case '?': // page up
  936. WriteAKeyToCMD( VK_PRIOR, VS_PRIOR, 0, ENHANCED_KEY );
  937. break;
  938. case '/': // page down
  939. WriteAKeyToCMD( VK_NEXT, VS_NEXT, 0, ENHANCED_KEY );
  940. break;
  941. case '1': // F1
  942. WriteAKeyToCMD( VK_F1, VK_F1, 0, ENHANCED_KEY );
  943. break;
  944. case '2': // F2
  945. WriteAKeyToCMD( VK_F2, VK_F2, 0, ENHANCED_KEY );
  946. break;
  947. case '3': // F3
  948. WriteAKeyToCMD( VK_F3, VS_F3, 0, ENHANCED_KEY );
  949. break;
  950. case '4': // F4
  951. WriteAKeyToCMD( VK_F4, VS_F4, 0, ENHANCED_KEY );
  952. break;
  953. case '5': // F5
  954. WriteAKeyToCMD( VK_F5, VS_F5, 0, ENHANCED_KEY );
  955. break;
  956. case '6': // F6
  957. WriteAKeyToCMD( VK_F6, VS_F6, 0, ENHANCED_KEY );
  958. break;
  959. case '7': // F7
  960. WriteAKeyToCMD( VK_F7, VS_F7, 0, ENHANCED_KEY );
  961. break;
  962. case '8': // F8
  963. WriteAKeyToCMD( VK_F8, VS_F8, 0, ENHANCED_KEY );
  964. break;
  965. case '9': // F9
  966. WriteAKeyToCMD( VK_F9, VS_F9, 0, ENHANCED_KEY );
  967. break;
  968. case '0': // F10
  969. WriteAKeyToCMD( VK_F10, VS_F10, 0, ENHANCED_KEY );
  970. break;
  971. case '!': // F11
  972. WriteAKeyToCMD( VK_F11, VS_F11, 0, ENHANCED_KEY );
  973. break;
  974. case '@': // F12
  975. WriteAKeyToCMD( VK_F12, VS_F12, 0, ENHANCED_KEY );
  976. break;
  977. case CTRLA:
  978. m_dwInputSequenceState = IP_ESC_CTRL_A_RCVD;
  979. //
  980. // Mark when we received this sequence
  981. //
  982. TimedEscSequenceTickCount = GetTickCount();
  983. break;
  984. case CTRLS:
  985. m_dwInputSequenceState = IP_ESC_CTRL_S_RCVD;
  986. //
  987. // Mark when we received this sequence
  988. //
  989. TimedEscSequenceTickCount = GetTickCount();
  990. break;
  991. case CTRLC:
  992. m_dwInputSequenceState = IP_ESC_CTRL_C_RCVD;
  993. //
  994. // Mark when we received this sequence
  995. //
  996. TimedEscSequenceTickCount = GetTickCount();
  997. break;
  998. default:
  999. //
  1000. // Write already received escape as it is and return false
  1001. //
  1002. WriteAKeyToCMD( VK_ESCAPE, VS_ESCAPE, ESC, ENHANCED_KEY );
  1003. bRetVal = false;
  1004. break;
  1005. }
  1006. break;
  1007. case IP_ESC_BRACKET_RCVD:
  1008. m_dwInputSequenceState = IP_INIT;
  1009. switch( cCurrentChar )
  1010. {
  1011. case 'A':
  1012. WriteAKeyToCMD( VK_UP, VS_UP, 0, ENHANCED_KEY );
  1013. break;
  1014. case 'B':
  1015. WriteAKeyToCMD( VK_DOWN, VS_DOWN, 0, ENHANCED_KEY );
  1016. break;
  1017. case 'C':
  1018. WriteAKeyToCMD( VK_RIGHT, VS_RIGHT, 0, ENHANCED_KEY );
  1019. break;
  1020. case 'D':
  1021. WriteAKeyToCMD( VK_LEFT, VS_LEFT, 0, ENHANCED_KEY );
  1022. break;
  1023. default:
  1024. //
  1025. // Send the <esc>[ characters through since a valid sequence
  1026. // was not recognized
  1027. //
  1028. WriteAKeyToCMD( VK_ESCAPE, VS_ESCAPE, ESC, ENHANCED_KEY );
  1029. WriteAKeyToCMD( VS_LEFT_BRACKET, VS_LEFT_BRACKET, '[', 0 );
  1030. //
  1031. // Not any special char
  1032. //
  1033. bRetVal = false;
  1034. break;
  1035. }
  1036. break;
  1037. case IP_ESC_CTRL_A_ESC_RCVD:
  1038. m_dwInputSequenceState = IP_INIT;
  1039. switch (cCurrentChar) {
  1040. case CTRLA:
  1041. //
  1042. // If we arrived here within 2 seconds
  1043. // then we should process the current character as an alt sequence
  1044. // otherwise, there is nothing to do
  1045. //
  1046. if (IsValidControlSequence()) {
  1047. //
  1048. // Send: <alt-pressed><alt-released>
  1049. //
  1050. // Normally this should be benign, but some apps may
  1051. // respond to this - for instance move the user to the menu
  1052. // bar
  1053. //
  1054. WriteAKeyToCMD( TRUE, VK_MENU, VS_MENU, 0, ENHANCED_KEY );
  1055. WriteAKeyToCMD( FALSE, VK_MENU, VS_MENU, 0, ENHANCED_KEY );
  1056. break;
  1057. }
  1058. //
  1059. // if the <esc><ctrl-a><esc><ctrl-a> sequenced timed-out,
  1060. // then fall through and do the default behavior.
  1061. //
  1062. default:
  1063. //
  1064. // We either timed-out after the <esc><ctrl-a><esc>
  1065. // or we received the following sequence:
  1066. //
  1067. // <esc><ctrl-a><esc>X
  1068. //
  1069. // We know that the <esc><ctrl-a><esc> was valid because
  1070. // we arrived here. Hence, in either case the translation
  1071. // should be:
  1072. //
  1073. // <alt-esc>X
  1074. //
  1075. WriteAKeyToCMD( TRUE, VK_MENU, VS_MENU, 0, ENHANCED_KEY );
  1076. WriteAKeyToCMD(ESC);
  1077. WriteAKeyToCMD( FALSE, VK_MENU, VS_MENU, 0, ENHANCED_KEY );
  1078. //
  1079. // Send the current character (X) through to be processed normally
  1080. //
  1081. bRetVal = false;
  1082. break;
  1083. }
  1084. break;
  1085. case IP_ESC_CTRL_A_RCVD:
  1086. m_dwInputSequenceState = IP_INIT;
  1087. switch (cCurrentChar) {
  1088. case ESC:
  1089. //
  1090. // If we arrived here within 2 seconds of receiving the ctrl-a
  1091. // then we should process the current character as an alt sequence
  1092. // otherwise, there is nothing to do
  1093. //
  1094. if (IsValidControlSequence()) {
  1095. //
  1096. // We need to move to the <esc><ctrl-a><esc> state
  1097. //
  1098. m_dwInputSequenceState = IP_ESC_CTRL_A_ESC_RCVD;
  1099. //
  1100. // Mark when we received this sequence
  1101. //
  1102. TimedEscSequenceTickCount = GetTickCount();
  1103. } else {
  1104. //Not any special char
  1105. bRetVal = false;
  1106. }
  1107. break;
  1108. default:
  1109. //
  1110. // If we arrived here within 2 seconds of receiving the ctrl-a
  1111. // then we should process the current character as an alt sequence
  1112. // otherwise, there is nothing to do
  1113. //
  1114. if (IsValidControlSequence()) {
  1115. WriteAKeyToCMD( TRUE, VK_MENU, VS_MENU, 0, ENHANCED_KEY );
  1116. WriteAKeyToCMD(cCurrentChar);
  1117. WriteAKeyToCMD( FALSE, VK_MENU, VS_MENU, 0, ENHANCED_KEY );
  1118. } else {
  1119. //Not any special char
  1120. bRetVal = false;
  1121. }
  1122. break;
  1123. }
  1124. break;
  1125. case IP_ESC_CTRL_C_RCVD:
  1126. m_dwInputSequenceState = IP_INIT;
  1127. //
  1128. // If we arrived here within 2 seconds of receiving the ctrl-c
  1129. // then we should process the current character as an alt sequence
  1130. // otherwise, there is nothing to do
  1131. //
  1132. if (IsValidControlSequence()) {
  1133. WriteAKeyToCMD( TRUE, VK_CONTROL, VS_CONTROL, 0, ENHANCED_KEY );
  1134. WriteAKeyToCMD(cCurrentChar);
  1135. WriteAKeyToCMD( FALSE, VK_CONTROL, VS_CONTROL, 0, ENHANCED_KEY );
  1136. } else {
  1137. //Not any special char
  1138. bRetVal = false;
  1139. }
  1140. break;
  1141. case IP_ESC_CTRL_S_RCVD:
  1142. m_dwInputSequenceState = IP_INIT;
  1143. //
  1144. // If we arrived here within 2 seconds of receiving the ctrl-c
  1145. // then we should process the current character as an alt sequence
  1146. // otherwise, there is nothing to do
  1147. //
  1148. if (IsValidControlSequence()) {
  1149. WriteAKeyToCMD( TRUE, VK_SHIFT, VS_SHIFT, 0, ENHANCED_KEY );
  1150. WriteAKeyToCMD(cCurrentChar);
  1151. WriteAKeyToCMD( FALSE, VK_SHIFT, VS_SHIFT, 0, ENHANCED_KEY );
  1152. } else {
  1153. //Not any special char
  1154. bRetVal = false;
  1155. }
  1156. break;
  1157. default:
  1158. //Should not happen
  1159. ASSERT( 0 );
  1160. }
  1161. return bRetVal;
  1162. }
  1163. BOOL
  1164. CVTUTF8Scraper::SendBytes(
  1165. PUCHAR pucBuf,
  1166. DWORD dwLength
  1167. )
  1168. /*++
  1169. Routine Description:
  1170. This routine sends an array of bytes to the IoHandler.
  1171. Arguments:
  1172. pucBuf - the array to send
  1173. dwLength - the # of bytes to send
  1174. Return Value:
  1175. Status
  1176. Security:
  1177. internal --> external
  1178. we are sending internal data to remote user
  1179. --*/
  1180. {
  1181. ASSERT(pucBuf);
  1182. return m_IoHandler->Write(
  1183. pucBuf,
  1184. dwLength
  1185. );
  1186. }
  1187. BOOL
  1188. CVTUTF8Scraper::SendString(
  1189. PWCHAR pwch
  1190. )
  1191. /*++
  1192. Routine Description:
  1193. This routine sends a WCHAR string to the IoHandler.
  1194. Arguments:
  1195. pwch - the string to send
  1196. Return Value:
  1197. Status
  1198. --*/
  1199. {
  1200. ASSERT(pwch);
  1201. return SendBytes(
  1202. ( PUCHAR )pwch,
  1203. (ULONG)(wcslen(pwch) * sizeof(WCHAR))
  1204. );
  1205. }
  1206. BOOL
  1207. CVTUTF8Scraper::SendColorInfo(
  1208. WORD wAttributes
  1209. )
  1210. /*++
  1211. Routine Description:
  1212. This routine assembles a VT-UTF8 encoded color attibutes command
  1213. and sends it to the IoHandler
  1214. Arguments:
  1215. wAttributes - the attributes to encode
  1216. Return Value:
  1217. Status
  1218. --*/
  1219. {
  1220. BOOL bSuccess;
  1221. //
  1222. // default
  1223. //
  1224. bSuccess = FALSE;
  1225. do {
  1226. if( wAttributes & BACKGROUND_INTENSITY )
  1227. {
  1228. //do nothing.
  1229. //There is no equivalent capability on vtutf8
  1230. NOTHING;
  1231. }
  1232. if( wAttributes & FOREGROUND_INTENSITY )
  1233. {
  1234. if( !fBold )
  1235. {
  1236. SGR( szSGRStr, 1 ); //Bold
  1237. bSuccess = SendString( szSGRStr );
  1238. if (! bSuccess) {
  1239. break;
  1240. }
  1241. fBold = true;
  1242. }
  1243. }
  1244. else
  1245. {
  1246. if( fBold )
  1247. {
  1248. SGR( szSGRStr, 22 ); //Bold off
  1249. bSuccess = SendString( szSGRStr );
  1250. if (! bSuccess) {
  1251. break;
  1252. }
  1253. fBold = false;
  1254. }
  1255. }
  1256. WORD wColor = 0;
  1257. if( wAttributes & FOREGROUND_BLUE )
  1258. {
  1259. wColor = ( WORD )( wColor | 0x0004 );
  1260. }
  1261. if( wAttributes & FOREGROUND_GREEN )
  1262. {
  1263. wColor = ( WORD )( wColor | 0x0002 );
  1264. }
  1265. if( wAttributes & FOREGROUND_RED )
  1266. {
  1267. wColor = ( WORD )( wColor | 0x0001 );
  1268. }
  1269. wColor += 30; //Base value for foreground colors
  1270. SGR( szSGRStr, wColor );
  1271. bSuccess = SendString( szSGRStr );
  1272. if (! bSuccess) {
  1273. break;
  1274. }
  1275. //WORD wColor = 0;
  1276. wColor = 0;
  1277. if( wAttributes & BACKGROUND_BLUE )
  1278. {
  1279. wColor = ( WORD )( wColor | 0x0004 );
  1280. }
  1281. if( wAttributes & BACKGROUND_GREEN )
  1282. {
  1283. wColor = ( WORD )( wColor | 0x0002 );
  1284. }
  1285. if( wAttributes & BACKGROUND_RED )
  1286. {
  1287. wColor = ( WORD )( wColor | 0x0001 );
  1288. }
  1289. wColor += 40; //Base value for Background colors
  1290. SGR( szSGRStr, wColor );
  1291. bSuccess = SendString( szSGRStr );
  1292. } while ( FALSE );
  1293. return bSuccess;
  1294. }
  1295. #define COMPARE_ROWS(currentRow, lastSeenRow, result) \
  1296. for(i = 0; i < wCols; ++i ) \
  1297. { \
  1298. if( pCurrent[ ( currentRow ) * wCols + i].Char.UnicodeChar != \
  1299. pLastSeen[ ( lastSeenRow ) * wCols + i].Char.UnicodeChar ) \
  1300. {\
  1301. (result) = 0; \
  1302. break;\
  1303. } \
  1304. if( ( wDefaultAttributes != pCurrent[ ( currentRow ) * wCols + i]. \
  1305. Attributes ) && \
  1306. ( pCurrent[ ( currentRow ) * wCols + i].Attributes != \
  1307. pLastSeen[ ( lastSeenRow ) * wCols + i].Attributes ) ) \
  1308. { \
  1309. (result) = 0; \
  1310. break; \
  1311. } \
  1312. }
  1313. //row, column are over the wire should be w.r.t screen.
  1314. //So, +1 for both row, column
  1315. #define POSITION_CURSOR( row, column ) \
  1316. ASSERT(row <= 23); \
  1317. { \
  1318. CursorMove( \
  1319. lpszCMResultsBuffer, \
  1320. ( WORD ) ( ( row ) + 1 ), \
  1321. ( WORD ) ( ( column ) + 1 ) \
  1322. ); \
  1323. bSuccess = SendString( lpszCMResultsBuffer ); \
  1324. if (!bSuccess) { \
  1325. break; \
  1326. } \
  1327. }
  1328. //
  1329. // Send columns [begin -- end] characters on <row>
  1330. //
  1331. // Note: Because we are modeling a unicode console, we have to be careful about
  1332. // what we decide to represent as vtutf8. Characters that take up more than
  1333. // one screen cell in the console have to be processed so we don't send
  1334. // redundant data.
  1335. //
  1336. // Hence, we only send a character if the cell contains:
  1337. //
  1338. // 1. a single byte character
  1339. // 2. the first position of a unicode character
  1340. // You can tell the first character position of a unicode characeter
  1341. // because the cells are enumarated. The enumeration is determined by:
  1342. //
  1343. // enum = (CHAR_INFO.Attributes & 0x0000ff00) >> 8
  1344. //
  1345. #if 0
  1346. //
  1347. // Very noisy debug version
  1348. //
  1349. #define SEND_ROW( row, begin, end ) \
  1350. { \
  1351. CHAR_INFO chi; \
  1352. UCHAR x; \
  1353. WCHAR blob[256]; \
  1354. wsprintf(blob,L"\r\n(row=%d:begin=%d:end=%d)\r\n", row, begin, end); \
  1355. OutputDebugString(blob); \
  1356. for(LONG c = ( begin ); c < ( end ); ++c ) { \
  1357. if( wExistingAttributes != pCurrent[( row ) * wCols + c].Attributes ) { \
  1358. wExistingAttributes = pCurrent[ ( row ) * wCols + c].Attributes; \
  1359. wDefaultAttributes = ( WORD )~0; \
  1360. bSuccess = SendColorInfo( wExistingAttributes ); \
  1361. if (!bSuccess) { \
  1362. break; \
  1363. } \
  1364. wsprintf(blob,L"(Color:%x)", wExistingAttributes); \
  1365. OutputDebugString(blob); \
  1366. } \
  1367. chi = pCurrent[ ( row ) * wCols + c]; \
  1368. x = (UCHAR)((chi.Attributes & 0x0000ff00) >> 8); \
  1369. wsprintf(blob,L"(%x)", chi.Char.UnicodeChar); \
  1370. OutputDebugString(blob); \
  1371. if (x < 2) { \
  1372. bSuccess = SendChar( chi.Char.UnicodeChar ); \
  1373. if (!bSuccess) { \
  1374. break; \
  1375. } \
  1376. } \
  1377. } \
  1378. }
  1379. #else
  1380. #define SEND_ROW( row, begin, end ) \
  1381. { \
  1382. CHAR_INFO chi; \
  1383. UCHAR x; \
  1384. for(LONG c = ( begin ); c < ( end ); ++c ) { \
  1385. if( wExistingAttributes != pCurrent[( row ) * wCols + c].Attributes ) { \
  1386. wExistingAttributes = pCurrent[ ( row ) * wCols + c].Attributes; \
  1387. wDefaultAttributes = ( WORD )~0; \
  1388. bSuccess = SendColorInfo( wExistingAttributes ); \
  1389. if (!bSuccess) { \
  1390. break; \
  1391. } \
  1392. } \
  1393. chi = pCurrent[ ( row ) * wCols + c]; \
  1394. x = (UCHAR)((chi.Attributes & 0x0000ff00) >> 8); \
  1395. if (x < 2) { \
  1396. bSuccess = SendChar( chi.Char.UnicodeChar ); \
  1397. if (!bSuccess) { \
  1398. break; \
  1399. } \
  1400. } \
  1401. } \
  1402. }
  1403. #endif
  1404. #define GET_DEFAULT_COLOR \
  1405. if( wDefaultAttributes == 0 ) \
  1406. { \
  1407. wDefaultAttributes = pCurrent[ 0 ].Attributes; \
  1408. wExistingAttributes = pCurrent[ 0 ].Attributes; \
  1409. }
  1410. #define IS_BLANK( row, col ) \
  1411. ( pCurrent[ ( row ) * wCols + ( col ) ].Char.UnicodeChar == ' ' )
  1412. #define IS_DIFFERENT_COLOR( row, col, attribs ) \
  1413. ( pCurrent[ ( row ) * wCols + ( col ) ].Attributes != ( attribs ) )
  1414. #define IS_CHANGE_IN_COLOR( row, col ) \
  1415. ( pCurrent[ ( row ) * wCols + ( col ) ].Attributes != \
  1416. pLastSeen[ ( row ) * wCols + ( col ) ].Attributes )
  1417. #define IS_CHANGE_IN_CHAR( row, col ) \
  1418. ( pCurrent[ ( row ) * wCols + ( col ) ].Char.UnicodeChar != \
  1419. pLastSeen[ ( row ) * wCols + ( col )].Char.UnicodeChar )
  1420. BOOL
  1421. CVTUTF8Scraper::CompareAndUpdate(
  1422. WORD wRows,
  1423. WORD wCols,
  1424. PCHAR_INFO pCurrent,
  1425. PCHAR_INFO pLastSeen,
  1426. PCONSOLE_SCREEN_BUFFER_INFO pCSBI,
  1427. PCONSOLE_SCREEN_BUFFER_INFO pLastCSBI
  1428. )
  1429. /*++
  1430. Routine Description:
  1431. This routine does the core work for scraping the screen.
  1432. Algorithm:
  1433. This routine does a row-by-row comparision.
  1434. If a row is found to be different, it figures out which region
  1435. of the row is different and sends that sub-row piece.
  1436. Arguments:
  1437. wRows - the # of rows to scrape
  1438. wCols - the # of cols to scrape
  1439. pCurrent - the current scraper buffer
  1440. pLastSeen - the last scraper buffer
  1441. pCSBI - the current Console Screen Buffer Info
  1442. pLastCSBI - the current Console Screen Buffer Info
  1443. Return Value:
  1444. TRUE - success
  1445. FALSE - otherwise
  1446. --*/
  1447. {
  1448. INT i;
  1449. WORD wRow;
  1450. WORD wCol;
  1451. INT iStartCol;
  1452. INT iEndCol;
  1453. BOOL fBlankLine;
  1454. COORD coordDest;
  1455. COORD coordOrigin;
  1456. SMALL_RECT srSource;
  1457. BOOL DifferenceFound;
  1458. BOOL bSuccess;
  1459. //
  1460. // default: we succeeded
  1461. //
  1462. bSuccess = TRUE;
  1463. //
  1464. // Default: no difference found
  1465. //
  1466. DifferenceFound = false;
  1467. //
  1468. //
  1469. //
  1470. GET_DEFAULT_COLOR;
  1471. //
  1472. // Read the console character matrix
  1473. //
  1474. ASSERT(wCols <= m_wMaxCols);
  1475. ASSERT(wRows <= m_wMaxRows);
  1476. coordDest.X = wCols;
  1477. coordDest.Y = wRows;
  1478. coordOrigin.X = 0;
  1479. coordOrigin.Y = 0;
  1480. srSource.Left = 0;
  1481. srSource.Top = 0;
  1482. srSource.Right = ( WORD ) ( wCols - 1 );
  1483. srSource.Bottom = ( WORD ) ( wRows - 1 );
  1484. bSuccess = ReadConsoleOutput(
  1485. m_hConBufOut,
  1486. pCurrent,
  1487. coordDest,
  1488. coordOrigin,
  1489. &srSource
  1490. );
  1491. if( !bSuccess )
  1492. {
  1493. return ( FALSE );
  1494. }
  1495. //
  1496. // Search the current and last screen buffers for differences.
  1497. //
  1498. wRow = wCol = 0;
  1499. while ( wRow < wRows ) {
  1500. //
  1501. // Compare the current row (wRow)
  1502. //
  1503. if( memcmp( &pCurrent[wRow * wCols],
  1504. &pLastSeen[wRow * wCols],
  1505. wCols * sizeof( CHAR_INFO ) ) != 0
  1506. ) {
  1507. //
  1508. // A difference was found
  1509. //
  1510. DifferenceFound = true;
  1511. //
  1512. // Initialize the difference tracking markers
  1513. //
  1514. iStartCol = -1;
  1515. iEndCol = -1;
  1516. fBlankLine = true;
  1517. //
  1518. // Determine where in the current row the rows differ
  1519. //
  1520. for (i = 0 ; i < wCols; ++i ) {
  1521. if( IS_DIFFERENT_COLOR( wRow, i, wDefaultAttributes ) &&
  1522. IS_CHANGE_IN_COLOR( wRow, i )
  1523. ) {
  1524. if( iStartCol == -1 )
  1525. {
  1526. iStartCol = i;
  1527. }
  1528. iEndCol = i;
  1529. fBlankLine = false;
  1530. }
  1531. if( IS_CHANGE_IN_CHAR( wRow, i ) ) {
  1532. if( iStartCol == -1 ) {
  1533. iStartCol = i;
  1534. }
  1535. iEndCol = i;
  1536. }
  1537. if( fBlankLine && !IS_BLANK( wRow, i ) ) {
  1538. fBlankLine = false;
  1539. }
  1540. }
  1541. if( fBlankLine ) {
  1542. POSITION_CURSOR( wRow, 0 );
  1543. CursorEOL();
  1544. coExpectedCursor.Y = wRow;
  1545. coExpectedCursor.X = 0;
  1546. } else if( iStartCol != -1 ) {
  1547. if( wRow != coExpectedCursor.Y || iStartCol != coExpectedCursor.X ) {
  1548. POSITION_CURSOR( wRow, iStartCol );
  1549. coExpectedCursor.X = ( SHORT )iStartCol;
  1550. coExpectedCursor.Y = wRow;
  1551. }
  1552. SEND_ROW( wRow, iStartCol, iEndCol+1 );
  1553. coExpectedCursor.X = ( SHORT ) ( coExpectedCursor.X + iEndCol - iStartCol + 1 );
  1554. }
  1555. }
  1556. ++wRow;
  1557. }
  1558. //
  1559. // If we found a difference while doing the screen compares
  1560. // or if the cursor moved in the console,
  1561. // then update the cursor position
  1562. //
  1563. if( DifferenceFound ||
  1564. ( memcmp( &pCSBI->dwCursorPosition, &pLastCSBI->dwCursorPosition, sizeof( COORD ) ) != 0 )
  1565. ) {
  1566. do {
  1567. //
  1568. // Move the cursor to where it's supposed to be
  1569. //
  1570. POSITION_CURSOR(
  1571. pCSBI->dwCursorPosition.Y,
  1572. pCSBI->dwCursorPosition.X
  1573. );
  1574. coExpectedCursor.X = pCSBI->dwCursorPosition.X;
  1575. coExpectedCursor.Y = pCSBI->dwCursorPosition.Y;
  1576. //
  1577. // Copy pCurrent onto pLastSeen
  1578. //
  1579. memcpy( pLastSeen, pCurrent, wCols * wRows * sizeof( CHAR_INFO ) );
  1580. memcpy( pLastCSBI, pCSBI, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) );
  1581. //
  1582. // There is a difference between this screen and the last,
  1583. // so we have written the changes. Now that we are done
  1584. // writing the changes, we need to flush the data we've written.
  1585. //
  1586. m_IoHandler->Flush();
  1587. } while ( FALSE );
  1588. }
  1589. return( bSuccess );
  1590. }
  1591. LPWSTR
  1592. CVTUTF8Scraper::CursorMove(
  1593. OUT LPWSTR pCmsResult,
  1594. IN WORD y,
  1595. IN WORD x
  1596. )
  1597. /*++
  1598. Routine Description:
  1599. This routine assembles an Ansi escape sequence to position
  1600. the cursor on a Ansi terminal
  1601. Arguments:
  1602. lpCmsResult - on exit, buffer contains the string
  1603. y - the Y cursor position
  1604. x - the X cursor position
  1605. Return Value:
  1606. the pointer into the result buffer at the NULL
  1607. --*/
  1608. {
  1609. #if DBG
  1610. PWCHAR pBegin;
  1611. pBegin = pCmsResult;
  1612. #endif
  1613. ASSERT(pCmsResult);
  1614. //
  1615. // Assemble the prefix sequence prefix
  1616. //
  1617. pCmsResult[0] = 0x1B; // <esc>
  1618. pCmsResult[1] = L'[';
  1619. pCmsResult++;
  1620. pCmsResult++;
  1621. //
  1622. // Translate the Y position
  1623. //
  1624. // 1 or 2 characters consumed
  1625. //
  1626. pCmsResult = FastIToA_10(
  1627. y,
  1628. pCmsResult
  1629. );
  1630. //
  1631. // Insert the delimiter
  1632. //
  1633. *pCmsResult = L';';
  1634. pCmsResult++;
  1635. //
  1636. // Translate the X position
  1637. //
  1638. // 1 or 2 characters consumed
  1639. //
  1640. pCmsResult = FastIToA_10(
  1641. x,
  1642. pCmsResult
  1643. );
  1644. //
  1645. // Insert the suffix
  1646. //
  1647. *pCmsResult = L'H';
  1648. pCmsResult++;
  1649. //
  1650. // Terminate the string
  1651. //
  1652. *pCmsResult = UNICODE_NULL;
  1653. //
  1654. // make sure we have a valid string length
  1655. //
  1656. ASSERT(wcslen(pBegin) <= CM_STRING_LENGTH - 1);
  1657. return ( pCmsResult );
  1658. }