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.

2522 lines
62 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. vtutf8chan.c
  5. Abstract:
  6. Routines for managing channels in the sac.
  7. Author:
  8. Sean Selitrennikoff (v-seans) Sept, 2000.
  9. Brian Guarraci (briangu) March, 2001.
  10. Revision History:
  11. --*/
  12. #include "sac.h"
  13. //
  14. // Macro to validate the VTUTF8 Screen matrix coordinates
  15. //
  16. #define ASSERT_CHANNEL_ROW_COL(_Channel) \
  17. ASSERT(_Channel->CursorRow >= 0); \
  18. ASSERT(_Channel->CursorRow < SAC_VTUTF8_ROW_HEIGHT); \
  19. ASSERT(_Channel->CursorCol >= 0); \
  20. ASSERT(_Channel->CursorCol < SAC_VTUTF8_COL_WIDTH);
  21. //
  22. // VTUTF8 Attribute flags
  23. //
  24. // Note: We use bit flags in the UCHAR
  25. // that containts the attributes.
  26. // Hence, there can be up to 8 attributes.
  27. //
  28. //#define VTUTF8_ATTRIBUTES_OFF 0x1
  29. #define VTUTF8_ATTRIBUTE_BLINK 0x1
  30. #define VTUTF8_ATTRIBUTE_BOLD 0x2
  31. #define VTUTF8_ATTRIBUTE_INVERSE 0x4
  32. //
  33. // Internal VTUTF8 emulator command codes
  34. //
  35. typedef enum _SAC_ESCAPE_CODE {
  36. CursorUp,
  37. CursorDown,
  38. CursorRight,
  39. CursorLeft,
  40. AttributesOff,
  41. BlinkOn,
  42. BlinkOff,
  43. BoldOn,
  44. BoldOff,
  45. InverseOn,
  46. InverseOff,
  47. BackTab,
  48. ClearToEol,
  49. ClearToBol,
  50. ClearLine,
  51. ClearToEos,
  52. ClearToBos,
  53. ClearScreen,
  54. SetCursorPosition,
  55. SetScrollRegion,
  56. SetColor,
  57. SetBackgroundColor,
  58. SetForegroundColor,
  59. SetColorAndAttribute
  60. } SAC_ESCAPE_CODE, *PSAC_ESCAPE_CODE;
  61. //
  62. // Structure for assembling well-defined
  63. // command sequences.
  64. //
  65. typedef struct _SAC_STATIC_ESCAPE_STRING {
  66. WCHAR String[10];
  67. ULONG StringLength;
  68. SAC_ESCAPE_CODE Code;
  69. } SAC_STATIC_ESCAPE_STRING, *PSAC_STATIC_ESCAPE_STRING;
  70. //
  71. // The well-defined escape sequences.
  72. //
  73. // Note: add <esc>[YYYm sequences below (in consume escape sequences)
  74. // rather than here
  75. // Note: try to keep this list small since it gets iterated through
  76. // for every escape sequence consumed.
  77. // Note: it would be interesting to order these by hit frequency
  78. //
  79. SAC_STATIC_ESCAPE_STRING SacStaticEscapeStrings[] = {
  80. {L"[A", sizeof(L"[A")/sizeof(WCHAR)-1, CursorUp},
  81. {L"[B", sizeof(L"[B")/sizeof(WCHAR)-1, CursorDown},
  82. {L"[C", sizeof(L"[C")/sizeof(WCHAR)-1, CursorRight},
  83. {L"[D", sizeof(L"[D")/sizeof(WCHAR)-1, CursorLeft},
  84. {L"[0Z", sizeof(L"[0Z")/sizeof(WCHAR)-1, BackTab},
  85. {L"[K", sizeof(L"[K")/sizeof(WCHAR)-1, ClearToEol},
  86. {L"[1K", sizeof(L"[1K")/sizeof(WCHAR)-1, ClearToBol},
  87. {L"[2K", sizeof(L"[2K")/sizeof(WCHAR)-1, ClearLine},
  88. {L"[J", sizeof(L"[J")/sizeof(WCHAR)-1, ClearToEos},
  89. {L"[1J", sizeof(L"[1J")/sizeof(WCHAR)-1, ClearToBos},
  90. {L"[2J", sizeof(L"[2J")/sizeof(WCHAR)-1, ClearScreen}
  91. };
  92. //
  93. // Global defines for a default vtutf8 terminal. May be used by clients to size the
  94. // local monitor to match the headless monitor.
  95. //
  96. #define ANSI_TERM_DEFAULT_ATTRIBUTES 0
  97. #define ANSI_TERM_DEFAULT_BKGD_COLOR 40
  98. #define ANSI_TERM_DEFAULT_TEXT_COLOR 37
  99. //
  100. // Enumerated ANSI escape sequences
  101. //
  102. typedef enum _ANSI_CMD {
  103. ANSICmdClearDisplay,
  104. ANSICmdClearToEndOfDisplay,
  105. ANSICmdClearToEndOfLine,
  106. ANSICmdSetColor,
  107. ANSICmdPositionCursor,
  108. ANSICmdDisplayAttributesOff,
  109. ANSICmdDisplayInverseVideoOn,
  110. ANSICmdDisplayInverseVideoOff,
  111. ANSICmdDisplayBlinkOn,
  112. ANSICmdDisplayBlinkOff,
  113. ANSICmdDisplayBoldOn,
  114. ANSICmdDisplayBoldOff
  115. } ANSI_CMD, *PANSI_CMD;
  116. //
  117. // HeadlessCmdSetColor:
  118. // Input structure: FgColor, BkgColor: Both colors set according to ANSI terminal
  119. // definitons.
  120. //
  121. typedef struct _ANSI_CMD_SET_COLOR {
  122. ULONG FgColor;
  123. ULONG BkgColor;
  124. } ANSI_CMD_SET_COLOR, *PANSI_CMD_SET_COLOR;
  125. //
  126. // ANSICmdPositionCursor:
  127. // Input structure: Row, Column: Both values are zero base, with upper left being (1, 1).
  128. //
  129. typedef struct _ANSI_CMD_POSITION_CURSOR {
  130. ULONG X;
  131. ULONG Y;
  132. } ANSI_CMD_POSITION_CURSOR, *PANSI_CMD_POSITION_CURSOR;
  133. NTSTATUS
  134. VTUTF8ChannelProcessAttributes(
  135. IN PSAC_CHANNEL Channel,
  136. IN UCHAR Attributes
  137. );
  138. NTSTATUS
  139. VTUTF8ChannelAnsiDispatch(
  140. IN PSAC_CHANNEL Channel,
  141. IN ANSI_CMD Command,
  142. IN PVOID InputBuffer OPTIONAL,
  143. IN SIZE_T InputBufferSize OPTIONAL
  144. );
  145. VOID
  146. VTUTF8ChannelSetIBufferIndex(
  147. IN PSAC_CHANNEL Channel,
  148. IN ULONG IBufferIndex
  149. );
  150. ULONG
  151. VTUTF8ChannelGetIBufferIndex(
  152. IN PSAC_CHANNEL Channel
  153. );
  154. NTSTATUS
  155. VTUTF8ChannelOInit(
  156. PSAC_CHANNEL Channel
  157. )
  158. /*++
  159. Routine Description:
  160. Initialize the Output buffer
  161. Arguments:
  162. Channel - the channel to initialize
  163. Return Value:
  164. Status
  165. --*/
  166. {
  167. ULONG R;
  168. ULONG C;
  169. PSAC_SCREEN_BUFFER ScreenBuffer;
  170. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
  171. //
  172. // initialize the screen buffer
  173. //
  174. Channel->CurrentAttr = ANSI_TERM_DEFAULT_ATTRIBUTES;
  175. Channel->CurrentBg = ANSI_TERM_DEFAULT_BKGD_COLOR;
  176. Channel->CurrentFg = ANSI_TERM_DEFAULT_TEXT_COLOR;
  177. //
  178. // Get the output buffer
  179. //
  180. ScreenBuffer = (PSAC_SCREEN_BUFFER)Channel->OBuffer;
  181. //
  182. // Initialize all the vtutf8 elements to the default state
  183. //
  184. for (R = 0; R < SAC_VTUTF8_ROW_HEIGHT; R++) {
  185. for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++) {
  186. ScreenBuffer->Element[R][C].Value = ' ';
  187. ScreenBuffer->Element[R][C].BgColor = ANSI_TERM_DEFAULT_BKGD_COLOR;
  188. ScreenBuffer->Element[R][C].FgColor = ANSI_TERM_DEFAULT_TEXT_COLOR;
  189. }
  190. }
  191. return STATUS_SUCCESS;
  192. }
  193. NTSTATUS
  194. VTUTF8ChannelCreate(
  195. OUT PSAC_CHANNEL Channel
  196. )
  197. /*++
  198. Routine Description:
  199. This routine allocates a channel and returns a pointer to it.
  200. Arguments:
  201. Channel - The resulting channel.
  202. OpenChannelCmd - All the parameters for the new channel
  203. Return Value:
  204. STATUS_SUCCESS if successful, else the appropriate error code.
  205. --*/
  206. {
  207. NTSTATUS Status;
  208. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
  209. do {
  210. //
  211. // Allocate our output buffer
  212. //
  213. Channel->OBuffer = ALLOCATE_POOL(sizeof(SAC_SCREEN_BUFFER), GENERAL_POOL_TAG);
  214. ASSERT(Channel->OBuffer);
  215. if (!Channel->OBuffer) {
  216. Status = STATUS_NO_MEMORY;
  217. break;
  218. }
  219. //
  220. // Allocate our input buffer
  221. //
  222. Channel->IBuffer = (PUCHAR)ALLOCATE_POOL(SAC_RAW_OBUFFER_SIZE, GENERAL_POOL_TAG);
  223. ASSERT(Channel->IBuffer);
  224. if (!Channel->IBuffer) {
  225. Status = STATUS_NO_MEMORY;
  226. break;
  227. }
  228. //
  229. // Initialize the output buffer
  230. //
  231. Status = VTUTF8ChannelOInit(Channel);
  232. if (!NT_SUCCESS(Status)) {
  233. break;
  234. }
  235. //
  236. // Neither buffer has any new data
  237. //
  238. ChannelSetIBufferHasNewData(Channel, FALSE);
  239. ChannelSetOBufferHasNewData(Channel, FALSE);
  240. } while ( FALSE );
  241. //
  242. // Cleanup if necessary
  243. //
  244. if (!NT_SUCCESS(Status)) {
  245. if (Channel->OBuffer) {
  246. FREE_POOL(&Channel->OBuffer);
  247. }
  248. if (Channel->IBuffer) {
  249. FREE_POOL(&Channel->IBuffer);
  250. }
  251. }
  252. return Status;
  253. }
  254. NTSTATUS
  255. VTUTF8ChannelDestroy(
  256. IN OUT PSAC_CHANNEL Channel
  257. )
  258. /*++
  259. Routine Description:
  260. This routine closes a channel.
  261. Arguments:
  262. Channel - The channel to be closed
  263. Return Value:
  264. STATUS_SUCCESS if successful, else the appropriate error code.
  265. --*/
  266. {
  267. NTSTATUS Status;
  268. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
  269. //
  270. // Free the dynamically allocated memory
  271. //
  272. if (Channel->OBuffer) {
  273. FREE_POOL(&(Channel->OBuffer));
  274. Channel->OBuffer = NULL;
  275. }
  276. if (Channel->IBuffer) {
  277. FREE_POOL(&(Channel->IBuffer));
  278. Channel->IBuffer = NULL;
  279. }
  280. //
  281. // Now that we've done our channel specific destroy,
  282. // Call the general channel destroy
  283. //
  284. Status = ChannelDestroy(Channel);
  285. return STATUS_SUCCESS;
  286. }
  287. NTSTATUS
  288. VTUTF8ChannelORead(
  289. IN PSAC_CHANNEL Channel,
  290. IN PUCHAR Buffer,
  291. IN ULONG BufferSize,
  292. OUT PULONG ByteCount
  293. )
  294. {
  295. UNREFERENCED_PARAMETER(Channel);
  296. UNREFERENCED_PARAMETER(Buffer);
  297. UNREFERENCED_PARAMETER(ByteCount);
  298. UNREFERENCED_PARAMETER(BufferSize);
  299. return STATUS_NOT_IMPLEMENTED;
  300. }
  301. NTSTATUS
  302. VTUTF8ChannelOEcho(
  303. IN PSAC_CHANNEL Channel,
  304. IN PCUCHAR String,
  305. IN ULONG Size
  306. )
  307. /*++
  308. Routine Description:
  309. This routine puts the string out the ansi port.
  310. Arguments:
  311. Channel - Previously created channel.
  312. String - Output string.
  313. Length - The # of String bytes to process
  314. Return Value:
  315. STATUS_SUCCESS if successful, otherwise status
  316. --*/
  317. {
  318. NTSTATUS Status;
  319. BOOLEAN bStatus;
  320. ULONG Length;
  321. ULONG i;
  322. ULONG k;
  323. ULONG j;
  324. ULONG TranslatedCount;
  325. ULONG UTF8TranslationSize;
  326. PCWSTR pwch;
  327. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
  328. ASSERT_STATUS(String, STATUS_INVALID_PARAMETER_2);
  329. //
  330. // Note: Simply echoing out the String buffer will ONLY work
  331. // reliably if our VTUTF8 emulation does EXACTLY the same emulation
  332. // as the remote client. If our interpretation of the incoming stream
  333. // differs, there will be a discrepency between the two screen images.
  334. // For instance, if we do line wrapping (col becomes 0, and row++) and
  335. // the remote client does not, echoing of String will fail to reflect
  336. // the line wrapping end users (client) will only see the correct (our)
  337. // representation of our VTUTF8 screen when the switch away and come back,
  338. // thereby causing a screen redraw
  339. //
  340. // One possible way around this problem would be to put 'dirty' bits into our vtutf8 screen
  341. // buffer for each cell. At this point, we could scan the buffer for changes
  342. // and send the appropriate updates rather than just blindly echoing String.
  343. //
  344. //
  345. // Determine the total # of WCHARs to process
  346. //
  347. Length = Size / sizeof(WCHAR);
  348. //
  349. // Do nothing if there is nothing to do
  350. //
  351. if (Length == 0) {
  352. return STATUS_SUCCESS;
  353. }
  354. //
  355. // Point to the beginning of the string
  356. //
  357. pwch = (PCWSTR)String;
  358. //
  359. // Default: we were successful
  360. //
  361. Status = STATUS_SUCCESS;
  362. //
  363. // Divide the incoming buffer into blocks of length
  364. // MAX_UTF8_ENCODE_BLOCK_LENGTH.
  365. //
  366. do {
  367. //
  368. // Determine the remainder
  369. //
  370. k = Length % MAX_UTF8_ENCODE_BLOCK_LENGTH;
  371. if (k > 0) {
  372. //
  373. // Translate the first k characters
  374. //
  375. bStatus = SacTranslateUnicodeToUtf8(
  376. pwch,
  377. k,
  378. Utf8ConversionBuffer,
  379. Utf8ConversionBufferSize,
  380. &UTF8TranslationSize,
  381. &TranslatedCount
  382. );
  383. //
  384. // If this assert hits, it is probably caused by
  385. // a premature NULL termination in the incoming string
  386. //
  387. ASSERT(k == TranslatedCount);
  388. if (!bStatus) {
  389. Status = STATUS_UNSUCCESSFUL;
  390. break;
  391. }
  392. //
  393. // Send the UTF8 encoded characters
  394. //
  395. Status = IoMgrWriteData(
  396. Channel,
  397. (PUCHAR)Utf8ConversionBuffer,
  398. UTF8TranslationSize
  399. );
  400. if (!NT_SUCCESS(Status)) {
  401. break;
  402. }
  403. //
  404. // Adjust the pwch to account for the sent length
  405. //
  406. pwch += k;
  407. }
  408. //
  409. // Determine the # of blocks we can process
  410. //
  411. j = Length / MAX_UTF8_ENCODE_BLOCK_LENGTH;
  412. //
  413. // Translate each WCHAR to UTF8 individually. This way,
  414. // no matter how big the String is, we don't run into
  415. // buffer size problems (it just might take a while).
  416. //
  417. for (i = 0; i < j; i++) {
  418. //
  419. // Encode the next block
  420. //
  421. bStatus = SacTranslateUnicodeToUtf8(
  422. pwch,
  423. MAX_UTF8_ENCODE_BLOCK_LENGTH,
  424. Utf8ConversionBuffer,
  425. Utf8ConversionBufferSize,
  426. &UTF8TranslationSize,
  427. &TranslatedCount
  428. );
  429. //
  430. // If this assert hits, it is probably caused by
  431. // a premature NULL termination in the incoming string
  432. //
  433. ASSERT(MAX_UTF8_ENCODE_BLOCK_LENGTH == TranslatedCount);
  434. ASSERT(UTF8TranslationSize > 0);
  435. if (! bStatus) {
  436. Status = STATUS_UNSUCCESSFUL;
  437. break;
  438. }
  439. //
  440. // Adjust the pwch to account for the sent length
  441. //
  442. pwch += MAX_UTF8_ENCODE_BLOCK_LENGTH;
  443. //
  444. // Send the UTF8 encoded characters
  445. //
  446. Status = IoMgrWriteData(
  447. Channel,
  448. (PUCHAR)Utf8ConversionBuffer,
  449. UTF8TranslationSize
  450. );
  451. if (! NT_SUCCESS(Status)) {
  452. break;
  453. }
  454. }
  455. } while ( FALSE );
  456. //
  457. // Validate that the pwch pointer stopped at the end of the buffer
  458. //
  459. ASSERT(pwch == (PWSTR)(String + Size));
  460. //
  461. // If we were successful, flush the channel's data in the iomgr
  462. //
  463. if (NT_SUCCESS(Status)) {
  464. Status = IoMgrFlushData(Channel);
  465. }
  466. return Status;
  467. }
  468. NTSTATUS
  469. VTUTF8ChannelOWrite(
  470. IN PSAC_CHANNEL Channel,
  471. IN PCUCHAR String,
  472. IN ULONG Size
  473. )
  474. /*++
  475. Routine Description:
  476. This routine takes a string and prints it to the specified channel. If the channel
  477. is the currently active channel, it puts the string out the ansi port as well.
  478. Note: Current Channel lock must be held by caller
  479. Arguments:
  480. Channel - Previously created channel.
  481. String - Output string.
  482. Length - The # of String bytes to process
  483. Return Value:
  484. STATUS_SUCCESS if successful, else the appropriate error code.
  485. --*/
  486. {
  487. NTSTATUS Status;
  488. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
  489. ASSERT_STATUS(String, STATUS_INVALID_PARAMETER_2);
  490. do {
  491. //
  492. // call the appropriate "printscreen" depending on the channel type
  493. //
  494. // Note: this may be done more cleanly with function pointers and using
  495. // a common function prototype. The ChannelPrintStringIntoScreenBuffer
  496. // function could translate the uchar buffer into a wchar buffer internally
  497. //
  498. Status = VTUTF8ChannelOWrite2(
  499. Channel,
  500. (PCWSTR)String,
  501. Size / sizeof(WCHAR)
  502. );
  503. if (! NT_SUCCESS(Status)) {
  504. break;
  505. }
  506. //
  507. // if the current channel is the active channel and the user has selected
  508. // to display this channel, relay the output directly to the user
  509. //
  510. if (IoMgrIsWriteEnabled(Channel) && ChannelSentToScreen(Channel)){
  511. Status = VTUTF8ChannelOEcho(
  512. Channel,
  513. String,
  514. Size
  515. );
  516. } else {
  517. //
  518. // this is not the current channel,
  519. // hence, this channel has new data
  520. //
  521. ChannelSetOBufferHasNewData(Channel, TRUE);
  522. }
  523. } while ( FALSE );
  524. return Status;
  525. }
  526. NTSTATUS
  527. VTUTF8ChannelOWrite2(
  528. IN PSAC_CHANNEL Channel,
  529. IN PCWSTR String,
  530. IN ULONG Length
  531. )
  532. /*++
  533. Routine Description:
  534. This routine takes a string and prints it into the screen buffer. This makes this
  535. routine, essentially, a VTUTF8 emulator.
  536. Arguments:
  537. Channel - Previously created channel.
  538. String - String to print.
  539. Length - Length of the string to write
  540. Return Value:
  541. STATUS_SUCCESS if successful, else the appropriate error code.
  542. --*/
  543. {
  544. ULONG i;
  545. ULONG Consumed;
  546. ULONG R, C;
  547. PCWSTR pwch;
  548. PSAC_SCREEN_BUFFER ScreenBuffer;
  549. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
  550. ASSERT_STATUS(String, STATUS_INVALID_PARAMETER_2);
  551. ASSERT_CHANNEL_ROW_COL(Channel);
  552. //
  553. // Get the VTUTF8 Screen Buffer
  554. //
  555. ScreenBuffer = (PSAC_SCREEN_BUFFER)Channel->OBuffer;
  556. //
  557. // Iterate through the string and do an internal vtutf8 emulation,
  558. // storing the "screen" in the Screen Buffer
  559. //
  560. for (i = 0; i < Length; i++) {
  561. //
  562. // Get the next character to process
  563. //
  564. pwch = &(String[i]);
  565. if (*pwch == '\033') { // escape char
  566. //
  567. // Note: if the String doesn't contain a complete escape sequence
  568. // then when we consume the escape sequence, we'll fail to
  569. // recognize the sequence and drop it. Then, when the rest
  570. // of the sequence follows, it will appear as text.
  571. //
  572. // FIX: this requires a better overall parsing engine that preserves state...
  573. //
  574. Consumed = VTUTF8ChannelConsumeEscapeSequence(Channel, pwch);
  575. if (Consumed != 0) {
  576. //
  577. // Adding Consumed moves us to just after the escape sequence
  578. // just consumed. However, we need to subract 1 because we
  579. // are about to add one due to the for loop
  580. //
  581. i += Consumed - 1;
  582. continue;
  583. } else {
  584. //
  585. // Ignore the escape
  586. //
  587. i++;
  588. continue;
  589. }
  590. } else {
  591. //
  592. // First, if this is a special character, process it.
  593. //
  594. //
  595. // Return
  596. //
  597. if (*pwch == '\n') {
  598. Channel->CursorCol = 0;
  599. continue;
  600. }
  601. //
  602. // Linefeed
  603. //
  604. if (*pwch == '\r') {
  605. Channel->CursorRow++;
  606. //
  607. // If we scrolled off the bottom, move everything up one line and clear
  608. // the bottom line.
  609. //
  610. if (Channel->CursorRow >= SAC_VTUTF8_ROW_HEIGHT) {
  611. for (R = 0; R < SAC_VTUTF8_ROW_HEIGHT - 1; R++) {
  612. ASSERT(R+1 < SAC_VTUTF8_ROW_HEIGHT);
  613. for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++) {
  614. ScreenBuffer->Element[R][C] = ScreenBuffer->Element[R+1][C];
  615. }
  616. }
  617. ASSERT(R == SAC_VTUTF8_ROW_HEIGHT-1);
  618. for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++) {
  619. RtlZeroMemory(&(ScreenBuffer->Element[R][C]), sizeof(SAC_SCREEN_ELEMENT));
  620. }
  621. Channel->CursorRow--;
  622. }
  623. ASSERT_CHANNEL_ROW_COL(Channel);
  624. continue;
  625. }
  626. //
  627. // Tab
  628. //
  629. if (*pwch == '\t') {
  630. ASSERT_CHANNEL_ROW_COL(Channel);
  631. C = 4 - Channel->CursorCol % 4;
  632. for (; C != 0 ; C--) {
  633. ASSERT_CHANNEL_ROW_COL(Channel);
  634. ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].Attr = Channel->CurrentAttr;
  635. ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].BgColor = Channel->CurrentBg;
  636. ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].FgColor = Channel->CurrentFg;
  637. ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].Value = ' ';
  638. Channel->CursorCol++;
  639. if (Channel->CursorCol >= SAC_VTUTF8_COL_WIDTH) { // no line wrap.
  640. Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1;
  641. }
  642. }
  643. ASSERT_CHANNEL_ROW_COL(Channel);
  644. continue;
  645. }
  646. //
  647. // Backspace or delete character
  648. //
  649. if ((*pwch == 0x8) || (*pwch == 0x7F)) {
  650. if (Channel->CursorCol > 0) {
  651. Channel->CursorCol--;
  652. }
  653. ASSERT_CHANNEL_ROW_COL(Channel);
  654. continue;
  655. }
  656. //
  657. // We just consume all the rest of non-printable characters.
  658. //
  659. if (*pwch < ' ') {
  660. continue;
  661. }
  662. //
  663. // All normal characters end up here.
  664. //
  665. ASSERT_CHANNEL_ROW_COL(Channel);
  666. ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].Attr = Channel->CurrentAttr;
  667. ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].BgColor = Channel->CurrentBg;
  668. ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].FgColor = Channel->CurrentFg;
  669. ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].Value = *pwch;
  670. Channel->CursorCol++;
  671. if (Channel->CursorCol == SAC_VTUTF8_COL_WIDTH) { // no line wrap.
  672. Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1;
  673. }
  674. ASSERT_CHANNEL_ROW_COL(Channel);
  675. }
  676. }
  677. ASSERT_CHANNEL_ROW_COL(Channel);
  678. return STATUS_SUCCESS;
  679. }
  680. //
  681. // This macro calculates the # of escape sequence characters
  682. //
  683. // Note: the compiler accounts for the fact
  684. // that _p and _s are PWCHARs, so we don't need to
  685. // divide by sizeof(WCHAR).
  686. //
  687. #define CALC_CONSUMED(_p,_s)\
  688. ((ULONG)((_p) - (_s)) + 1)
  689. ULONG
  690. VTUTF8ChannelConsumeEscapeSequence(
  691. IN PSAC_CHANNEL Channel,
  692. IN PCWSTR String
  693. )
  694. /*++
  695. Routine Description:
  696. This routine takes an escape sequence and process it, returning the number of
  697. character it consumed from the string. If the escape sequence is not a valid
  698. vtutf8 sequence, it returns 0.
  699. Note: if the String doesn't contain a complete escape sequence
  700. then when we consume the escape sequence, we'll fail to
  701. recognize the sequence and drop it. Then, when the rest
  702. of the sequence follows, it will appear as text.
  703. FIX: this requires a better overall parsing engine that preserves state...
  704. Arguments:
  705. Channel - Previously created channel.
  706. String - Escape sequence.
  707. Return Value:
  708. Number of characters consumed
  709. --*/
  710. {
  711. ULONG i;
  712. SAC_ESCAPE_CODE Code;
  713. PCWSTR pch;
  714. ULONG Consumed;
  715. ULONG Param1 = 0;
  716. ULONG Param2 = 0;
  717. ULONG Param3 = 0;
  718. PSAC_SCREEN_BUFFER ScreenBuffer;
  719. ASSERT(String[0] == '\033');
  720. //
  721. // Get the VTUTF8 Screen Buffer
  722. //
  723. ScreenBuffer = (PSAC_SCREEN_BUFFER)Channel->OBuffer;
  724. //
  725. // Check for one of the easy strings first.
  726. //
  727. for (i = 0; i < sizeof(SacStaticEscapeStrings)/sizeof(SAC_STATIC_ESCAPE_STRING); i++) {
  728. if (wcsncmp(&(String[1]),
  729. SacStaticEscapeStrings[i].String,
  730. SacStaticEscapeStrings[i].StringLength) == 0) {
  731. //
  732. // Populate the arguments for the function to process this code
  733. //
  734. Code = SacStaticEscapeStrings[i].Code;
  735. Param1 = 1;
  736. Param2 = 1;
  737. Param3 = 1;
  738. //
  739. // # chars consumed = length of escape string + <esc>
  740. //
  741. Consumed = SacStaticEscapeStrings[i].StringLength + 1;
  742. goto ProcessCode;
  743. }
  744. }
  745. //
  746. // Check for escape sequences with parameters.
  747. //
  748. if (String[1] != '[') {
  749. return 0;
  750. }
  751. pch = &(String[2]);
  752. //
  753. // look for '<esc>[X' codes
  754. //
  755. switch (*pch) {
  756. case 'A':
  757. Code = CursorUp;
  758. Consumed = CALC_CONSUMED(pch, String);
  759. goto ProcessCode;
  760. case 'B':
  761. Code = CursorDown;
  762. Consumed = CALC_CONSUMED(pch, String);
  763. goto ProcessCode;
  764. case 'C':
  765. Code = CursorLeft;
  766. Consumed = CALC_CONSUMED(pch, String);
  767. goto ProcessCode;
  768. case 'D':
  769. Code = CursorRight;
  770. Consumed = CALC_CONSUMED(pch, String);
  771. goto ProcessCode;
  772. case 'K':
  773. Code = ClearToEol;
  774. Consumed = CALC_CONSUMED(pch, String);
  775. goto ProcessCode;
  776. }
  777. //
  778. // if we made it here, there should be a # next
  779. //
  780. if (!VTUTF8ChannelScanForNumber(pch, &Param1)) {
  781. return 0;
  782. }
  783. //
  784. // Skip past the numbers
  785. //
  786. while ((*pch >= '0') && (*pch <= '9')) {
  787. pch++;
  788. }
  789. //
  790. // Check for set color
  791. //
  792. if (*pch == 'm') {
  793. switch (Param1) {
  794. case 0:
  795. Code = AttributesOff;
  796. break;
  797. case 1:
  798. Code = BoldOn;
  799. break;
  800. case 5:
  801. Code = BlinkOn;
  802. break;
  803. case 7:
  804. Code = InverseOn;
  805. break;
  806. case 22:
  807. Code = BoldOff;
  808. break;
  809. case 25:
  810. Code = BlinkOff;
  811. break;
  812. case 27:
  813. Code = InverseOff;
  814. break;
  815. default:
  816. if (Param1 >= 40 && Param1 <= 47) {
  817. Code = SetBackgroundColor;
  818. } else if (Param1 >= 30 && Param1 <= 39) {
  819. Code = SetForegroundColor;
  820. } else {
  821. //
  822. // This allows us to catch unhandled codes,
  823. // so we know they need to be supported
  824. //
  825. ASSERT(0);
  826. return 0;
  827. }
  828. break;
  829. }
  830. Consumed = CALC_CONSUMED(pch, String);
  831. goto ProcessCode;
  832. }
  833. if (*pch != ';') {
  834. return 0;
  835. }
  836. pch++;
  837. if (!VTUTF8ChannelScanForNumber(pch, &Param2)) {
  838. return 0;
  839. }
  840. //
  841. // Skip past the numbers
  842. //
  843. while ((*pch >= '0') && (*pch <= '9')) {
  844. pch++;
  845. }
  846. //
  847. // Check for set color
  848. //
  849. if (*pch == 'm') {
  850. Code = SetColor;
  851. Consumed = CALC_CONSUMED(pch, String);
  852. goto ProcessCode;
  853. }
  854. //
  855. // Check for set cursor position
  856. //
  857. if (*pch == 'H') {
  858. Code = SetCursorPosition;
  859. Consumed = CALC_CONSUMED(pch, String);
  860. goto ProcessCode;
  861. }
  862. if (*pch != ';') {
  863. return 0;
  864. }
  865. pch++;
  866. switch (*pch) {
  867. case 'H':
  868. case 'f':
  869. Code = SetCursorPosition;
  870. Consumed = CALC_CONSUMED(pch, String);
  871. goto ProcessCode;
  872. case 'r':
  873. Code = SetScrollRegion;
  874. Consumed = CALC_CONSUMED(pch, String);
  875. goto ProcessCode;
  876. }
  877. if (!VTUTF8ChannelScanForNumber(pch, &Param3)) {
  878. return 0;
  879. }
  880. //
  881. // Skip past the numbers
  882. //
  883. while ((*pch >= '0') && (*pch <= '9')) {
  884. pch++;
  885. }
  886. //
  887. // Check for set color and attribute
  888. //
  889. if (*pch == 'm') {
  890. Code = SetColorAndAttribute;
  891. Consumed = CALC_CONSUMED(pch, String);
  892. goto ProcessCode;
  893. }
  894. return 0;
  895. ProcessCode:
  896. ASSERT_CHANNEL_ROW_COL(Channel);
  897. switch (Code) {
  898. case CursorUp:
  899. if (Channel->CursorRow >= Param1) {
  900. Channel->CursorRow = (UCHAR)(Channel->CursorRow - (UCHAR)Param1);
  901. } else {
  902. Channel->CursorRow = 0;
  903. }
  904. ASSERT_CHANNEL_ROW_COL(Channel);
  905. break;
  906. case CursorDown:
  907. if ((Channel->CursorRow + Param1) < SAC_VTUTF8_ROW_HEIGHT) {
  908. Channel->CursorRow = (UCHAR)(Channel->CursorRow + (UCHAR)Param1);
  909. } else {
  910. Channel->CursorRow = SAC_VTUTF8_ROW_HEIGHT - 1;
  911. }
  912. ASSERT_CHANNEL_ROW_COL(Channel);
  913. break;
  914. case CursorLeft:
  915. if (Channel->CursorCol >= Param1) {
  916. Channel->CursorCol = (UCHAR)(Channel->CursorCol - (UCHAR)Param1);
  917. } else {
  918. Channel->CursorCol = 0;
  919. }
  920. ASSERT_CHANNEL_ROW_COL(Channel);
  921. break;
  922. case CursorRight:
  923. if ((Channel->CursorCol + Param1) < SAC_VTUTF8_COL_WIDTH) {
  924. Channel->CursorCol = (UCHAR)(Channel->CursorCol + (UCHAR)Param1);
  925. } else {
  926. Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1;
  927. }
  928. ASSERT_CHANNEL_ROW_COL(Channel);
  929. break;
  930. case AttributesOff:
  931. //
  932. // Reset to default attributes and colors
  933. //
  934. Channel->CurrentAttr = ANSI_TERM_DEFAULT_ATTRIBUTES;
  935. Channel->CurrentBg = ANSI_TERM_DEFAULT_BKGD_COLOR;
  936. Channel->CurrentFg = ANSI_TERM_DEFAULT_TEXT_COLOR;
  937. break;
  938. case BlinkOn:
  939. Channel->CurrentAttr |= VTUTF8_ATTRIBUTE_BLINK;
  940. break;
  941. case BlinkOff:
  942. Channel->CurrentAttr &= ~VTUTF8_ATTRIBUTE_BLINK;
  943. break;
  944. case BoldOn:
  945. Channel->CurrentAttr |= VTUTF8_ATTRIBUTE_BOLD;
  946. break;
  947. case BoldOff:
  948. Channel->CurrentAttr &= ~VTUTF8_ATTRIBUTE_BOLD;
  949. break;
  950. case InverseOn:
  951. Channel->CurrentAttr |= VTUTF8_ATTRIBUTE_INVERSE;
  952. break;
  953. case InverseOff:
  954. Channel->CurrentAttr &= ~VTUTF8_ATTRIBUTE_INVERSE;
  955. break;
  956. case BackTab:
  957. break;
  958. case ClearToEol:
  959. Param1 = Channel->CursorCol;
  960. Param2 = SAC_VTUTF8_COL_WIDTH;
  961. goto DoClearLine;
  962. case ClearToBol:
  963. Param1 = 0;
  964. Param2 = Channel->CursorCol + 1;
  965. goto DoClearLine;
  966. case ClearLine:
  967. Param1 = 0;
  968. Param2 = SAC_VTUTF8_COL_WIDTH;
  969. DoClearLine:
  970. for (i = Param1; i < Param2; i++) {
  971. ScreenBuffer->Element[Channel->CursorRow][i].Attr = Channel->CurrentAttr;
  972. ScreenBuffer->Element[Channel->CursorRow][i].FgColor = Channel->CurrentFg;
  973. ScreenBuffer->Element[Channel->CursorRow][i].BgColor = Channel->CurrentBg;
  974. ScreenBuffer->Element[Channel->CursorRow][i].Value = ' ';
  975. }
  976. break;
  977. case ClearToEos:
  978. //
  979. // Start with clearing this line from the current cursor position
  980. //
  981. Param3 = Channel->CursorCol;
  982. for (i = Channel->CursorRow; i < SAC_VTUTF8_ROW_HEIGHT; i++) {
  983. for (Param1 = Param3; Param1 < SAC_VTUTF8_COL_WIDTH; Param1++) {
  984. ScreenBuffer->Element[i][Param1].Attr = Channel->CurrentAttr;
  985. ScreenBuffer->Element[i][Param1].FgColor = Channel->CurrentFg;
  986. ScreenBuffer->Element[i][Param1].BgColor = Channel->CurrentBg;
  987. ScreenBuffer->Element[i][Param1].Value = ' ';
  988. }
  989. //
  990. // Then clear the entire line for all other lines
  991. //
  992. Param3 = 0;
  993. }
  994. break;
  995. case ClearToBos:
  996. //
  997. // Start by clearing all of the line
  998. //
  999. Param3 = SAC_VTUTF8_COL_WIDTH;
  1000. for (i = 0; i <= Channel->CursorRow; i++) {
  1001. if (i == Channel->CursorRow) {
  1002. Param3 = Channel->CursorCol;
  1003. }
  1004. for (Param1 = 0; Param1 < Param3; Param1++) {
  1005. ScreenBuffer->Element[i][Param1].Attr = Channel->CurrentAttr;
  1006. ScreenBuffer->Element[i][Param1].FgColor = Channel->CurrentFg;
  1007. ScreenBuffer->Element[i][Param1].BgColor = Channel->CurrentBg;
  1008. ScreenBuffer->Element[i][Param1].Value = ' ';
  1009. }
  1010. }
  1011. break;
  1012. case ClearScreen:
  1013. for (i = 0; i < SAC_VTUTF8_ROW_HEIGHT; i++) {
  1014. for (Param1 = 0; Param1 < SAC_VTUTF8_COL_WIDTH; Param1++) {
  1015. ScreenBuffer->Element[i][Param1].Attr = Channel->CurrentAttr;
  1016. ScreenBuffer->Element[i][Param1].FgColor = Channel->CurrentFg;
  1017. ScreenBuffer->Element[i][Param1].BgColor = Channel->CurrentBg;
  1018. ScreenBuffer->Element[i][Param1].Value = ' ';
  1019. }
  1020. }
  1021. break;
  1022. case SetCursorPosition:
  1023. Channel->CursorRow = (UCHAR)Param1; // I adjust below for 0-based array - don't subtract 1 here.
  1024. Channel->CursorCol = (UCHAR)Param2; // I adjust below for 0-based array - don't subtract 1 here.
  1025. if (Channel->CursorRow > SAC_VTUTF8_ROW_HEIGHT) {
  1026. Channel->CursorRow = SAC_VTUTF8_ROW_HEIGHT;
  1027. }
  1028. if (Channel->CursorRow >= 1) {
  1029. Channel->CursorRow--;
  1030. }
  1031. if (Channel->CursorCol > SAC_VTUTF8_COL_WIDTH) {
  1032. Channel->CursorCol = SAC_VTUTF8_COL_WIDTH;
  1033. }
  1034. if (Channel->CursorCol >= 1) {
  1035. Channel->CursorCol--;
  1036. }
  1037. ASSERT_CHANNEL_ROW_COL(Channel);
  1038. break;
  1039. case SetColor:
  1040. Channel->CurrentFg = (UCHAR)Param1;
  1041. Channel->CurrentBg = (UCHAR)Param2;
  1042. break;
  1043. case SetBackgroundColor:
  1044. Channel->CurrentBg = (UCHAR)Param1;
  1045. break;
  1046. case SetForegroundColor:
  1047. Channel->CurrentFg = (UCHAR)Param1;
  1048. break;
  1049. case SetColorAndAttribute:
  1050. Channel->CurrentAttr = (UCHAR)Param1;
  1051. Channel->CurrentFg = (UCHAR)Param2;
  1052. Channel->CurrentBg = (UCHAR)Param3;
  1053. break;
  1054. }
  1055. return Consumed;
  1056. }
  1057. NTSTATUS
  1058. VTUTF8ChannelOFlush(
  1059. IN PSAC_CHANNEL Channel
  1060. )
  1061. /*++
  1062. Routine Description:
  1063. Send the contents of the screen buffer to the remote terminal. This
  1064. is done by sending VTUTF8 codes to recreate the screen buffer on the
  1065. remote terminal.
  1066. Arguments:
  1067. Channel - Previously created channel.
  1068. Return Value:
  1069. STATUS_SUCCESS if successful, else the appropriate error code.
  1070. --*/
  1071. {
  1072. NTSTATUS Status;
  1073. BOOLEAN bStatus;
  1074. PWCHAR LocalBuffer;
  1075. UCHAR CurrentAttr;
  1076. UCHAR CurrentFg;
  1077. UCHAR CurrentBg;
  1078. ULONG R, C;
  1079. BOOLEAN RepositionCursor;
  1080. ANSI_CMD_SET_COLOR SetColor;
  1081. ANSI_CMD_POSITION_CURSOR SetCursor;
  1082. PSAC_SCREEN_BUFFER ScreenBuffer;
  1083. ULONG TranslatedCount;
  1084. ULONG UTF8TranslationSize;
  1085. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
  1086. //
  1087. // Get the VTUTF8 Screen Buffer
  1088. //
  1089. ScreenBuffer = (PSAC_SCREEN_BUFFER)Channel->OBuffer;
  1090. //
  1091. // Cursor offset on the screen
  1092. //
  1093. #define CURSOR_ROW_OFFSET 0
  1094. #define CURSOR_COL_OFFSET 0
  1095. //
  1096. // Allocate the local buffer
  1097. //
  1098. LocalBuffer = ALLOCATE_POOL(20*sizeof(WCHAR), GENERAL_POOL_TAG);
  1099. if (!LocalBuffer) {
  1100. Status = STATUS_NO_MEMORY;
  1101. goto VTUTF8ChannelOFlushCleanup;
  1102. }
  1103. //
  1104. // Clear the terminal screen.
  1105. //
  1106. Status = VTUTF8ChannelAnsiDispatch(
  1107. Channel,
  1108. ANSICmdClearDisplay,
  1109. NULL,
  1110. 0
  1111. );
  1112. if (! NT_SUCCESS(Status)) {
  1113. goto VTUTF8ChannelOFlushCleanup;
  1114. }
  1115. //
  1116. // Set the cursor to the top left
  1117. //
  1118. SetCursor.Y = CURSOR_ROW_OFFSET;
  1119. SetCursor.X = CURSOR_COL_OFFSET;
  1120. Status = VTUTF8ChannelAnsiDispatch(
  1121. Channel,
  1122. ANSICmdPositionCursor,
  1123. &SetCursor,
  1124. sizeof(ANSI_CMD_POSITION_CURSOR)
  1125. );
  1126. if (! NT_SUCCESS(Status)) {
  1127. goto VTUTF8ChannelOFlushCleanup;
  1128. }
  1129. //
  1130. // Reset the terminal attributes to defaults
  1131. //
  1132. Status = VTUTF8ChannelAnsiDispatch(
  1133. Channel,
  1134. ANSICmdDisplayAttributesOff,
  1135. NULL,
  1136. 0
  1137. );
  1138. if (! NT_SUCCESS(Status)) {
  1139. goto VTUTF8ChannelOFlushCleanup;
  1140. }
  1141. //
  1142. // Send starting attributes
  1143. //
  1144. CurrentAttr = Channel->CurrentAttr;
  1145. Status = VTUTF8ChannelProcessAttributes(
  1146. Channel,
  1147. CurrentAttr
  1148. );
  1149. if (! NT_SUCCESS(Status)) {
  1150. goto VTUTF8ChannelOFlushCleanup;
  1151. }
  1152. //
  1153. // Send starting colors.
  1154. //
  1155. CurrentBg = Channel->CurrentBg;
  1156. CurrentFg = Channel->CurrentFg;
  1157. SetColor.BkgColor = CurrentBg;
  1158. SetColor.FgColor = CurrentFg;
  1159. Status = VTUTF8ChannelAnsiDispatch(
  1160. Channel,
  1161. ANSICmdSetColor,
  1162. &SetColor,
  1163. sizeof(ANSI_CMD_SET_COLOR)
  1164. );
  1165. if (! NT_SUCCESS(Status)) {
  1166. goto VTUTF8ChannelOFlushCleanup;
  1167. }
  1168. //
  1169. // default: we don't need to reposition the cursor
  1170. //
  1171. RepositionCursor = FALSE;
  1172. //
  1173. // Send each character
  1174. //
  1175. for (R = 0; R < SAC_VTUTF8_ROW_HEIGHT; R++) {
  1176. for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++) {
  1177. if ((ScreenBuffer->Element[R][C].BgColor != CurrentBg) ||
  1178. (ScreenBuffer->Element[R][C].FgColor != CurrentFg)) {
  1179. //
  1180. // Change screen colors as necessary
  1181. //
  1182. if (RepositionCursor) {
  1183. SetCursor.Y = R + CURSOR_ROW_OFFSET;
  1184. SetCursor.X = C + CURSOR_COL_OFFSET;
  1185. Status = VTUTF8ChannelAnsiDispatch(
  1186. Channel,
  1187. ANSICmdPositionCursor,
  1188. &SetCursor,
  1189. sizeof(ANSI_CMD_POSITION_CURSOR)
  1190. );
  1191. if (! NT_SUCCESS(Status)) {
  1192. goto VTUTF8ChannelOFlushCleanup;
  1193. }
  1194. RepositionCursor = FALSE;
  1195. }
  1196. CurrentBg = ScreenBuffer->Element[R][C].BgColor;
  1197. CurrentFg = ScreenBuffer->Element[R][C].FgColor;
  1198. SetColor.BkgColor = CurrentBg;
  1199. SetColor.FgColor = CurrentFg;
  1200. Status = VTUTF8ChannelAnsiDispatch(
  1201. Channel,
  1202. ANSICmdSetColor,
  1203. &SetColor,
  1204. sizeof(ANSI_CMD_SET_COLOR)
  1205. );
  1206. if (! NT_SUCCESS(Status)) {
  1207. goto VTUTF8ChannelOFlushCleanup;
  1208. }
  1209. }
  1210. if (ScreenBuffer->Element[R][C].Attr != CurrentAttr) {
  1211. //
  1212. // Change attribute as necessary
  1213. //
  1214. if (RepositionCursor) {
  1215. SetCursor.Y = R + CURSOR_ROW_OFFSET;
  1216. SetCursor.X = C + CURSOR_COL_OFFSET;
  1217. Status = VTUTF8ChannelAnsiDispatch(
  1218. Channel,
  1219. ANSICmdPositionCursor,
  1220. &SetCursor,
  1221. sizeof(ANSI_CMD_POSITION_CURSOR)
  1222. );
  1223. if (! NT_SUCCESS(Status)) {
  1224. goto VTUTF8ChannelOFlushCleanup;
  1225. }
  1226. RepositionCursor = FALSE;
  1227. }
  1228. CurrentAttr = ScreenBuffer->Element[R][C].Attr;
  1229. Status = VTUTF8ChannelProcessAttributes(
  1230. Channel,
  1231. CurrentAttr
  1232. );
  1233. if (! NT_SUCCESS(Status)) {
  1234. goto VTUTF8ChannelOFlushCleanup;
  1235. }
  1236. }
  1237. //
  1238. // Send the character. Note: we can optimize the not-sending of
  1239. // space characters, if the clear screen was in the same
  1240. // color as the current color.
  1241. //
  1242. #if 0
  1243. if ((ScreenBuffer->Element[R][C].Value != ' ') ||
  1244. (CurrentAttr != 0) ||
  1245. (CurrentBg != ANSI_TERM_DEFAULT_BKGD_COLOR) ||
  1246. (CurrentFg != ANSI_TERM_DEFAULT_TEXT_COLOR)) {
  1247. #endif
  1248. {
  1249. if (RepositionCursor) {
  1250. SetCursor.Y = R + CURSOR_ROW_OFFSET;
  1251. SetCursor.X = C + CURSOR_COL_OFFSET;
  1252. Status = VTUTF8ChannelAnsiDispatch(
  1253. Channel,
  1254. ANSICmdPositionCursor,
  1255. &SetCursor,
  1256. sizeof(ANSI_CMD_POSITION_CURSOR)
  1257. );
  1258. if (! NT_SUCCESS(Status)) {
  1259. goto VTUTF8ChannelOFlushCleanup;
  1260. }
  1261. RepositionCursor = FALSE;
  1262. }
  1263. LocalBuffer[0] = ScreenBuffer->Element[R][C].Value;
  1264. LocalBuffer[1] = UNICODE_NULL;
  1265. bStatus = SacTranslateUnicodeToUtf8(
  1266. LocalBuffer,
  1267. 1,
  1268. Utf8ConversionBuffer,
  1269. Utf8ConversionBufferSize,
  1270. &UTF8TranslationSize,
  1271. &TranslatedCount
  1272. );
  1273. if (! bStatus) {
  1274. Status = STATUS_UNSUCCESSFUL;
  1275. goto VTUTF8ChannelOFlushCleanup;
  1276. }
  1277. //
  1278. // If the UTF8 encoded string is non-empty, send it
  1279. //
  1280. if (UTF8TranslationSize > 0) {
  1281. Status = IoMgrWriteData(
  1282. Channel,
  1283. (PUCHAR)Utf8ConversionBuffer,
  1284. UTF8TranslationSize
  1285. );
  1286. if (! NT_SUCCESS(Status)) {
  1287. goto VTUTF8ChannelOFlushCleanup;
  1288. }
  1289. }
  1290. }
  1291. #if 0
  1292. } else {
  1293. RepositionCursor = TRUE;
  1294. }
  1295. #endif
  1296. }
  1297. //
  1298. // Position the cursor on the new row
  1299. //
  1300. RepositionCursor = TRUE;
  1301. }
  1302. //
  1303. // Position cursor
  1304. //
  1305. SetCursor.Y = Channel->CursorRow + CURSOR_ROW_OFFSET;
  1306. SetCursor.X = Channel->CursorCol + CURSOR_COL_OFFSET;
  1307. Status = VTUTF8ChannelAnsiDispatch(
  1308. Channel,
  1309. ANSICmdPositionCursor,
  1310. &SetCursor,
  1311. sizeof(ANSI_CMD_POSITION_CURSOR)
  1312. );
  1313. if (! NT_SUCCESS(Status)) {
  1314. goto VTUTF8ChannelOFlushCleanup;
  1315. }
  1316. //
  1317. // Send current attributes
  1318. //
  1319. Status = VTUTF8ChannelProcessAttributes(
  1320. Channel,
  1321. Channel->CurrentAttr
  1322. );
  1323. if (! NT_SUCCESS(Status)) {
  1324. goto VTUTF8ChannelOFlushCleanup;
  1325. }
  1326. //
  1327. // Send current colors.
  1328. //
  1329. SetColor.BkgColor = Channel->CurrentBg;
  1330. SetColor.FgColor = Channel->CurrentFg;
  1331. Status = VTUTF8ChannelAnsiDispatch(
  1332. Channel,
  1333. ANSICmdSetColor,
  1334. &SetColor,
  1335. sizeof(ANSI_CMD_SET_COLOR)
  1336. );
  1337. if (! NT_SUCCESS(Status)) {
  1338. goto VTUTF8ChannelOFlushCleanup;
  1339. }
  1340. VTUTF8ChannelOFlushCleanup:
  1341. //
  1342. // If we were successful, flush the channel's data in the iomgr
  1343. //
  1344. if (NT_SUCCESS(Status)) {
  1345. Status = IoMgrFlushData(Channel);
  1346. }
  1347. //
  1348. // Free local resources
  1349. //
  1350. if (LocalBuffer) {
  1351. FREE_POOL(&LocalBuffer);
  1352. }
  1353. //
  1354. // If we have successfully flushed the obuffer,
  1355. // then we no longer have any new data
  1356. //
  1357. if (NT_SUCCESS(Status)) {
  1358. ChannelSetOBufferHasNewData(Channel, FALSE);
  1359. }
  1360. return Status;
  1361. }
  1362. NTSTATUS
  1363. VTUTF8ChannelIWrite(
  1364. IN PSAC_CHANNEL Channel,
  1365. IN PCUCHAR Buffer,
  1366. IN ULONG BufferSize
  1367. )
  1368. /*++
  1369. Routine Description:
  1370. This routine takes a single character and adds it to the buffered input for this channel.
  1371. Arguments:
  1372. Channel - Previously created channel.
  1373. Buffer - Incoming buffer of UCHARs
  1374. BufferSize - Incoming buffer size
  1375. Return Value:
  1376. STATUS_SUCCESS if successful, else the appropriate error code.
  1377. --*/
  1378. {
  1379. NTSTATUS Status;
  1380. BOOLEAN haveNewChar;
  1381. ULONG i;
  1382. BOOLEAN IBufferStatus;
  1383. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
  1384. ASSERT_STATUS(Buffer, STATUS_INVALID_PARAMETER_2);
  1385. ASSERT_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE);
  1386. //
  1387. // Make sure we aren't full
  1388. //
  1389. Status = VTUTF8ChannelIBufferIsFull(
  1390. Channel,
  1391. &IBufferStatus
  1392. );
  1393. if (! NT_SUCCESS(Status)) {
  1394. return Status;
  1395. }
  1396. //
  1397. // If there is no more room, then fail
  1398. //
  1399. if (IBufferStatus == TRUE) {
  1400. return STATUS_UNSUCCESSFUL;
  1401. }
  1402. //
  1403. // make sure there is enough room for the buffer
  1404. //
  1405. // Note: this prevents us from writing a portion of the buffer
  1406. // and then failing, leaving the caller in the state where
  1407. // it doesn't know how much of the buffer was written.
  1408. //
  1409. if ((SAC_VTUTF8_IBUFFER_SIZE - VTUTF8ChannelGetIBufferIndex(Channel)) < BufferSize) {
  1410. return STATUS_INSUFFICIENT_RESOURCES;
  1411. }
  1412. //
  1413. // default: we succeeded
  1414. //
  1415. Status = STATUS_SUCCESS;
  1416. for (i = 0; i < BufferSize; i++) {
  1417. //
  1418. // VTUTF8 channels receive UTF8 encoded Unicode, so
  1419. // translate the UTF8 byte by byte into Unicode
  1420. // as it's received. Only delcare that we have a new
  1421. // Unicode character if the a complete tranlsation
  1422. // from UTF8 --> Unicode took place.
  1423. //
  1424. haveNewChar = SacTranslateUtf8ToUnicode(
  1425. Buffer[i],
  1426. IncomingUtf8ConversionBuffer,
  1427. &IncomingUnicodeValue
  1428. );
  1429. //
  1430. // if a completed Unicode value was assembled, then we have a new character
  1431. //
  1432. if (haveNewChar) {
  1433. PWCHAR pwch;
  1434. pwch = (PWCHAR)&(Channel->IBuffer[VTUTF8ChannelGetIBufferIndex(Channel)]);
  1435. *pwch = IncomingUnicodeValue;
  1436. //
  1437. // update the buffer index
  1438. //
  1439. VTUTF8ChannelSetIBufferIndex(
  1440. Channel,
  1441. VTUTF8ChannelGetIBufferIndex(Channel) + sizeof(WCHAR)/sizeof(UCHAR)
  1442. );
  1443. }
  1444. }
  1445. //
  1446. // Fire the Has New Data event if specified
  1447. //
  1448. if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) {
  1449. ASSERT(Channel->HasNewDataEvent);
  1450. ASSERT(Channel->HasNewDataEventObjectBody);
  1451. ASSERT(Channel->HasNewDataEventWaitObjectBody);
  1452. KeSetEvent(
  1453. Channel->HasNewDataEventWaitObjectBody,
  1454. EVENT_INCREMENT,
  1455. FALSE
  1456. );
  1457. }
  1458. return STATUS_SUCCESS;
  1459. }
  1460. NTSTATUS
  1461. VTUTF8ChannelIRead(
  1462. IN PSAC_CHANNEL Channel,
  1463. IN PUCHAR Buffer,
  1464. IN ULONG BufferSize,
  1465. OUT PULONG ByteCount
  1466. )
  1467. /*++
  1468. Routine Description:
  1469. This routine takes the first character in the input buffer, removes and returns it. If
  1470. there is none, it returns 0x0.
  1471. Arguments:
  1472. Channel - Previously created channel.
  1473. Buffer - The buffer to read into
  1474. BufferSize - The size of the buffer
  1475. ByteCount - The # of bytes read
  1476. Return Value:
  1477. Status
  1478. --*/
  1479. {
  1480. ULONG CopyChars;
  1481. ULONG CopySize;
  1482. //
  1483. // initialize
  1484. //
  1485. CopyChars = 0;
  1486. CopySize = 0;
  1487. //
  1488. // Default: no bytes were read
  1489. //
  1490. *ByteCount = 0;
  1491. //
  1492. // Bail if there is no new data
  1493. //
  1494. if (Channel->IBufferLength(Channel) == 0) {
  1495. ASSERT(ChannelHasNewIBufferData(Channel) == FALSE);
  1496. return STATUS_SUCCESS;
  1497. }
  1498. //
  1499. // Caclulate the largest buffer size we can use (and need), and then calculate
  1500. // the number of characters this refers to.
  1501. //
  1502. CopySize = Channel->IBufferLength(Channel) * sizeof(WCHAR);
  1503. CopySize = CopySize > BufferSize ? BufferSize : CopySize;
  1504. CopyChars = CopySize / sizeof(WCHAR);
  1505. //
  1506. // recalc size in case there was rounding when calculating CopyChars
  1507. //
  1508. CopySize = CopyChars * sizeof(WCHAR);
  1509. //
  1510. // sanity check the copy size
  1511. //
  1512. ASSERT(CopyChars <= Channel->IBufferLength(Channel));
  1513. //
  1514. // Copy as much as we can from the ibuffer to the out-going buffer
  1515. //
  1516. RtlCopyMemory(Buffer, Channel->IBuffer, CopySize);
  1517. //
  1518. // Update the buffer index to account for the size we just copied
  1519. //
  1520. VTUTF8ChannelSetIBufferIndex(
  1521. Channel,
  1522. VTUTF8ChannelGetIBufferIndex(Channel) - CopySize
  1523. );
  1524. //
  1525. // If there is remaining data left in the Channel input buffer,
  1526. // shift it to the beginning
  1527. //
  1528. if (Channel->IBufferLength(Channel) > 0) {
  1529. RtlMoveMemory(&(Channel->IBuffer[0]),
  1530. &(Channel->IBuffer[CopySize]),
  1531. Channel->IBufferLength(Channel) * sizeof(WCHAR)
  1532. );
  1533. }
  1534. //
  1535. // Send back the # of bytes read
  1536. //
  1537. *ByteCount = CopySize;
  1538. return STATUS_SUCCESS;
  1539. }
  1540. BOOLEAN
  1541. VTUTF8ChannelScanForNumber(
  1542. IN PCWSTR pch,
  1543. OUT PULONG Number
  1544. )
  1545. /*++
  1546. Routine Description:
  1547. This routine takes a character stream and converts it into an integer.
  1548. Arguments:
  1549. pch - The character stream.
  1550. Number - The equivalent integer.
  1551. Return Value:
  1552. TRUE, if successful, else FALSE.
  1553. --*/
  1554. {
  1555. if ((*pch < '0') || (*pch > '9')) {
  1556. return FALSE;
  1557. }
  1558. *Number = 0;
  1559. while ((*pch >= '0') && (*pch <= '9')) {
  1560. *Number = *Number * 10;
  1561. *Number = *Number + (ULONG)(*pch - '0');
  1562. pch++;
  1563. }
  1564. return TRUE;
  1565. }
  1566. NTSTATUS
  1567. VTUTF8ChannelIBufferIsFull(
  1568. IN PSAC_CHANNEL Channel,
  1569. OUT BOOLEAN* BufferStatus
  1570. )
  1571. /*++
  1572. Routine Description:
  1573. Determine if the IBuffer is full
  1574. Arguments:
  1575. Channel - Previously created channel.
  1576. BufferStatus - on exit, TRUE if the buffer is full, otherwise FALSE
  1577. Return Value:
  1578. Status
  1579. --*/
  1580. {
  1581. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
  1582. *BufferStatus = (BOOLEAN)(VTUTF8ChannelGetIBufferIndex(Channel) >= (SAC_VTUTF8_IBUFFER_SIZE-1));
  1583. return STATUS_SUCCESS;
  1584. }
  1585. WCHAR
  1586. VTUTF8ChannelIReadLast(
  1587. IN PSAC_CHANNEL Channel
  1588. )
  1589. /*++
  1590. Routine Description:
  1591. This routine takes the last character in the input buffer, removes and returns it. If
  1592. there is none, it returns 0x0.
  1593. Arguments:
  1594. Channel - Previously created channel.
  1595. Return Value:
  1596. Last character in the input buffer.
  1597. --*/
  1598. {
  1599. WCHAR Char;
  1600. PWCHAR pwch;
  1601. ASSERT(Channel);
  1602. Char = UNICODE_NULL;
  1603. if (Channel->IBufferLength(Channel) > 0) {
  1604. VTUTF8ChannelSetIBufferIndex(
  1605. Channel,
  1606. VTUTF8ChannelGetIBufferIndex(Channel) - sizeof(WCHAR)/sizeof(UCHAR)
  1607. );
  1608. pwch = (PWCHAR)&Channel->IBuffer[VTUTF8ChannelGetIBufferIndex(Channel)];
  1609. Char = *pwch;
  1610. *pwch = UNICODE_NULL;
  1611. }
  1612. return Char;
  1613. }
  1614. ULONG
  1615. VTUTF8ChannelIBufferLength(
  1616. IN PSAC_CHANNEL Channel
  1617. )
  1618. /*++
  1619. Routine Description:
  1620. This routine determines the length of the input buffer, treating the input buffer
  1621. contents as a string
  1622. Arguments:
  1623. Channel - Previously created channel.
  1624. Return Value:
  1625. The length of the current input buffer
  1626. --*/
  1627. {
  1628. ASSERT(Channel);
  1629. return (VTUTF8ChannelGetIBufferIndex(Channel) / sizeof(WCHAR));
  1630. }
  1631. NTSTATUS
  1632. VTUTF8ChannelAnsiDispatch(
  1633. IN PSAC_CHANNEL Channel,
  1634. IN ANSI_CMD Command,
  1635. IN PVOID InputBuffer OPTIONAL,
  1636. IN SIZE_T InputBufferSize OPTIONAL
  1637. )
  1638. /*++
  1639. Routine Description:
  1640. Arguments:
  1641. Channel - The channel sending this escape sequence
  1642. Command - The command to execute.
  1643. Environment:
  1644. Status
  1645. --*/
  1646. {
  1647. NTSTATUS Status;
  1648. PUCHAR Tmp;
  1649. PUCHAR LocalBuffer;
  1650. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
  1651. //
  1652. // default: not using local buffer
  1653. //
  1654. LocalBuffer = NULL;
  1655. Tmp = NULL;
  1656. //
  1657. // Default: we succeeded
  1658. //
  1659. Status = STATUS_SUCCESS;
  1660. //
  1661. // Various output commands
  1662. //
  1663. switch (Command) {
  1664. case ANSICmdClearDisplay:
  1665. Tmp = (PUCHAR)"\033[2J";
  1666. break;
  1667. case ANSICmdClearToEndOfDisplay:
  1668. Tmp = (PUCHAR)"\033[0J";
  1669. break;
  1670. case ANSICmdClearToEndOfLine:
  1671. Tmp = (PUCHAR)"\033[0K";
  1672. break;
  1673. case ANSICmdDisplayAttributesOff:
  1674. Tmp = (PUCHAR)"\033[0m";
  1675. break;
  1676. case ANSICmdDisplayInverseVideoOn:
  1677. Tmp = (PUCHAR)"\033[7m";
  1678. break;
  1679. case ANSICmdDisplayInverseVideoOff:
  1680. Tmp = (PUCHAR)"\033[27m";
  1681. break;
  1682. case ANSICmdDisplayBlinkOn:
  1683. Tmp = (PUCHAR)"\033[5m";
  1684. break;
  1685. case ANSICmdDisplayBlinkOff:
  1686. Tmp = (PUCHAR)"\033[25m";
  1687. break;
  1688. case ANSICmdDisplayBoldOn:
  1689. Tmp = (PUCHAR)"\033[1m";
  1690. break;
  1691. case ANSICmdDisplayBoldOff:
  1692. Tmp = (PUCHAR)"\033[22m";
  1693. break;
  1694. case ANSICmdSetColor:
  1695. case ANSICmdPositionCursor: {
  1696. ULONG l;
  1697. //
  1698. // allocate tmp buffer
  1699. //
  1700. LocalBuffer = ALLOCATE_POOL(80*sizeof(UCHAR), GENERAL_POOL_TAG);
  1701. ASSERT_STATUS(LocalBuffer, STATUS_NO_MEMORY);
  1702. switch (Command) {
  1703. case ANSICmdSetColor:
  1704. if ((InputBuffer == NULL) ||
  1705. (InputBufferSize != sizeof(ANSI_CMD_SET_COLOR))) {
  1706. Status = STATUS_INVALID_PARAMETER;
  1707. break;
  1708. }
  1709. //
  1710. // Assemble set color command
  1711. //
  1712. #if 0
  1713. l = sprintf((LPSTR)LocalBuffer,
  1714. "\033[%d;%dm",
  1715. ((PANSI_CMD_SET_COLOR)InputBuffer)->BkgColor,
  1716. ((PANSI_CMD_SET_COLOR)InputBuffer)->FgColor
  1717. );
  1718. #else
  1719. //
  1720. // Break the color commands into to two commands.
  1721. //
  1722. // Note: we do this because this is much more likely
  1723. // to be implemented than the compound command.
  1724. //
  1725. l = sprintf((LPSTR)LocalBuffer,
  1726. "\033[%dm\033[%dm",
  1727. ((PANSI_CMD_SET_COLOR)InputBuffer)->BkgColor,
  1728. ((PANSI_CMD_SET_COLOR)InputBuffer)->FgColor
  1729. );
  1730. #endif
  1731. ASSERT((l+1)*sizeof(UCHAR) < 80);
  1732. Tmp = &(LocalBuffer[0]);
  1733. break;
  1734. case ANSICmdPositionCursor:
  1735. if ((InputBuffer == NULL) ||
  1736. (InputBufferSize != sizeof(ANSI_CMD_POSITION_CURSOR))) {
  1737. Status = STATUS_INVALID_PARAMETER;
  1738. break;
  1739. }
  1740. //
  1741. // Assemble position cursor command
  1742. //
  1743. l = sprintf((LPSTR)LocalBuffer,
  1744. "\033[%d;%dH",
  1745. ((PANSI_CMD_POSITION_CURSOR)InputBuffer)->Y + 1,
  1746. ((PANSI_CMD_POSITION_CURSOR)InputBuffer)->X + 1
  1747. );
  1748. ASSERT((l+1)*sizeof(UCHAR) < 80);
  1749. Tmp = &(LocalBuffer[0]);
  1750. break;
  1751. default:
  1752. Status = STATUS_INVALID_PARAMETER;
  1753. ASSERT(0);
  1754. break;
  1755. }
  1756. break;
  1757. }
  1758. default:
  1759. Status = STATUS_INVALID_PARAMETER;
  1760. break;
  1761. }
  1762. //
  1763. // Send the data if we were successful
  1764. //
  1765. if (NT_SUCCESS(Status)) {
  1766. ASSERT(Tmp);
  1767. if (Tmp) {
  1768. Status = IoMgrWriteData(
  1769. Channel,
  1770. Tmp,
  1771. (ULONG)(strlen((const char *)Tmp)*sizeof(UCHAR))
  1772. );
  1773. }
  1774. //
  1775. // If we were successful, flush the channel's data in the iomgr
  1776. //
  1777. if (NT_SUCCESS(Status)) {
  1778. Status = IoMgrFlushData(Channel);
  1779. }
  1780. }
  1781. if (LocalBuffer) {
  1782. FREE_POOL(&LocalBuffer);
  1783. }
  1784. return Status;
  1785. }
  1786. NTSTATUS
  1787. VTUTF8ChannelProcessAttributes(
  1788. IN PSAC_CHANNEL Channel,
  1789. IN UCHAR Attributes
  1790. )
  1791. /*++
  1792. Routine Description:
  1793. Arguments:
  1794. Returns:
  1795. --*/
  1796. {
  1797. NTSTATUS Status;
  1798. ANSI_CMD Cmd;
  1799. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
  1800. do {
  1801. #if 0
  1802. //
  1803. // Send the attributes off command
  1804. //
  1805. // Note: if this attribute is set,
  1806. // then we ignore the rest of the
  1807. // attributes
  1808. //
  1809. if (Attributes == VTUTF8_ATTRIBUTES_OFF) {
  1810. Status = VTUTF8ChannelAnsiDispatch(
  1811. Channel,
  1812. ANSICmdDisplayAttributesOff,
  1813. NULL,
  1814. 0
  1815. );
  1816. if (! NT_SUCCESS(Status)) {
  1817. break;
  1818. }
  1819. //
  1820. // There are no more attributes to check
  1821. //
  1822. break;
  1823. }
  1824. #endif
  1825. //
  1826. // Bold
  1827. //
  1828. Cmd = Attributes & VTUTF8_ATTRIBUTE_BOLD ?
  1829. ANSICmdDisplayBoldOn :
  1830. ANSICmdDisplayBoldOff;
  1831. Status = VTUTF8ChannelAnsiDispatch(
  1832. Channel,
  1833. Cmd,
  1834. NULL,
  1835. 0
  1836. );
  1837. if (! NT_SUCCESS(Status)) {
  1838. break;
  1839. }
  1840. //
  1841. // Blink
  1842. //
  1843. Cmd = Attributes & VTUTF8_ATTRIBUTE_BLINK ?
  1844. ANSICmdDisplayBlinkOn :
  1845. ANSICmdDisplayBlinkOff;
  1846. Status = VTUTF8ChannelAnsiDispatch(
  1847. Channel,
  1848. Cmd,
  1849. NULL,
  1850. 0
  1851. );
  1852. if (! NT_SUCCESS(Status)) {
  1853. break;
  1854. }
  1855. //
  1856. // Inverse video
  1857. //
  1858. Cmd = Attributes & VTUTF8_ATTRIBUTE_INVERSE ?
  1859. ANSICmdDisplayInverseVideoOn :
  1860. ANSICmdDisplayInverseVideoOff;
  1861. Status = VTUTF8ChannelAnsiDispatch(
  1862. Channel,
  1863. Cmd,
  1864. NULL,
  1865. 0
  1866. );
  1867. if (! NT_SUCCESS(Status)) {
  1868. break;
  1869. }
  1870. } while ( FALSE );
  1871. return Status;
  1872. }
  1873. ULONG
  1874. VTUTF8ChannelGetIBufferIndex(
  1875. IN PSAC_CHANNEL Channel
  1876. )
  1877. /*++
  1878. Routine Description:
  1879. Get teh ibuffer index
  1880. Arguments:
  1881. Channel - the channel to get the ibuffer index from
  1882. Environment:
  1883. The ibuffer index
  1884. --*/
  1885. {
  1886. ASSERT(Channel);
  1887. //
  1888. // Make sure the ibuffer index is atleast aligned to a WCHAR
  1889. //
  1890. ASSERT((Channel->IBufferIndex % sizeof(WCHAR)) == 0);
  1891. //
  1892. // Make sure the ibuffer index is in bounds
  1893. //
  1894. ASSERT(Channel->IBufferIndex < SAC_VTUTF8_IBUFFER_SIZE);
  1895. return Channel->IBufferIndex;
  1896. }
  1897. VOID
  1898. VTUTF8ChannelSetIBufferIndex(
  1899. IN PSAC_CHANNEL Channel,
  1900. IN ULONG IBufferIndex
  1901. )
  1902. /*++
  1903. Routine Description:
  1904. Set the ibuffer index
  1905. Arguments:
  1906. Channel - the channel to get the ibuffer index from
  1907. IBufferIndex - the new inbuffer index
  1908. Environment:
  1909. None
  1910. --*/
  1911. {
  1912. ASSERT(Channel);
  1913. //
  1914. // Make sure the ibuffer index is atleast aligned to a WCHAR
  1915. //
  1916. ASSERT((Channel->IBufferIndex % sizeof(WCHAR)) == 0);
  1917. //
  1918. // Make sure the ibuffer index is in bounds
  1919. //
  1920. ASSERT(Channel->IBufferIndex < SAC_VTUTF8_IBUFFER_SIZE);
  1921. //
  1922. // Set the index
  1923. //
  1924. Channel->IBufferIndex = IBufferIndex;
  1925. //
  1926. // Set the has new data flag accordingly
  1927. //
  1928. ChannelSetIBufferHasNewData(
  1929. Channel,
  1930. Channel->IBufferIndex == 0 ? FALSE : TRUE
  1931. );
  1932. //
  1933. // Additional checking if the index == 0
  1934. //
  1935. if (Channel->IBufferIndex == 0) {
  1936. //
  1937. // Clear the Has New Data event if specified
  1938. //
  1939. if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) {
  1940. ASSERT(Channel->HasNewDataEvent);
  1941. ASSERT(Channel->HasNewDataEventObjectBody);
  1942. ASSERT(Channel->HasNewDataEventWaitObjectBody);
  1943. KeClearEvent(Channel->HasNewDataEventWaitObjectBody);
  1944. }
  1945. }
  1946. }