Source code of Windows XP (NT5)
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.

1055 lines
25 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. spterm.c
  5. Abstract:
  6. Text setup support for terminals
  7. Author:
  8. Sean Selitrennikoff (v-seans) 25-May-1999
  9. Revision History:
  10. --*/
  11. #include "spprecmp.h"
  12. #include "ntddser.h"
  13. #pragma hdrstop
  14. #include <hdlsblk.h>
  15. #include <hdlsterm.h>
  16. #define MS_DSRCTSCD 0xB0 // Status bits for DSR, CTS and CD
  17. BOOLEAN HeadlessTerminalConnected = FALSE;
  18. UCHAR Utf8ConversionBuffer[80*3+1];
  19. PUCHAR TerminalBuffer = Utf8ConversionBuffer;
  20. WCHAR UnicodeScratchBuffer[80+1];
  21. //
  22. // Use these variables to decode incoming UTF8
  23. // data streams.
  24. //
  25. WCHAR IncomingUnicodeValue;
  26. UCHAR IncomingUtf8ConversionBuffer[3];
  27. BOOLEAN
  28. SpTranslateUnicodeToUtf8(
  29. PCWSTR SourceBuffer,
  30. UCHAR *DestinationBuffer
  31. )
  32. /*++
  33. Routine Description:
  34. translates a unicode buffer into a UTF8 version.
  35. Arguments:
  36. SourceBuffer - unicode buffer to be translated.
  37. DestinationBuffer - receives UTF8 version of same buffer.
  38. Return Value:
  39. TRUE - We successfully translated the Unicode value into its
  40. corresponding UTF8 encoding.
  41. FALSE - The translation failed.
  42. --*/
  43. {
  44. ULONG Count = 0;
  45. //
  46. // convert into UTF8 for actual transmission
  47. //
  48. // UTF-8 encodes 2-byte Unicode characters as follows:
  49. // If the first nine bits are zero (00000000 0xxxxxxx), encode it as one byte 0xxxxxxx
  50. // If the first five bits are zero (00000yyy yyxxxxxx), encode it as two bytes 110yyyyy 10xxxxxx
  51. // Otherwise (zzzzyyyy yyxxxxxx), encode it as three bytes 1110zzzz 10yyyyyy 10xxxxxx
  52. //
  53. DestinationBuffer[Count] = (UCHAR)'\0';
  54. while (*SourceBuffer) {
  55. if( (*SourceBuffer & 0xFF80) == 0 ) {
  56. //
  57. // if the top 9 bits are zero, then just
  58. // encode as 1 byte. (ASCII passes through unchanged).
  59. //
  60. DestinationBuffer[Count++] = (UCHAR)(*SourceBuffer & 0x7F);
  61. } else if( (*SourceBuffer & 0xF700) == 0 ) {
  62. //
  63. // if the top 5 bits are zero, then encode as 2 bytes
  64. //
  65. DestinationBuffer[Count++] = (UCHAR)((*SourceBuffer >> 6) & 0x1F) | 0xC0;
  66. DestinationBuffer[Count++] = (UCHAR)(*SourceBuffer & 0xBF) | 0x80;
  67. } else {
  68. //
  69. // encode as 3 bytes
  70. //
  71. DestinationBuffer[Count++] = (UCHAR)((*SourceBuffer >> 12) & 0xF) | 0xE0;
  72. DestinationBuffer[Count++] = (UCHAR)((*SourceBuffer >> 6) & 0x3F) | 0x80;
  73. DestinationBuffer[Count++] = (UCHAR)(*SourceBuffer & 0xBF) | 0x80;
  74. }
  75. SourceBuffer += 1;
  76. }
  77. DestinationBuffer[Count] = (UCHAR)'\0';
  78. return(TRUE);
  79. }
  80. BOOLEAN
  81. SpTranslateUtf8ToUnicode(
  82. UCHAR IncomingByte,
  83. UCHAR *ExistingUtf8Buffer,
  84. WCHAR *DestinationUnicodeVal
  85. )
  86. /*++
  87. Routine Description:
  88. Takes IncomingByte and concatenates it onto ExistingUtf8Buffer.
  89. Then attempts to decode the new contents of ExistingUtf8Buffer.
  90. Arguments:
  91. IncomingByte - New character to be appended onto
  92. ExistingUtf8Buffer.
  93. ExistingUtf8Buffer - running buffer containing incomplete UTF8
  94. encoded unicode value. When it gets full,
  95. we'll decode the value and return the
  96. corresponding Unicode value.
  97. Note that if we *do* detect a completed UTF8
  98. buffer and actually do a decode and return a
  99. Unicode value, then we will zero-fill the
  100. contents of ExistingUtf8Buffer.
  101. DestinationUnicodeVal - receives Unicode version of the UTF8 buffer.
  102. Note that if we do *not* detect a completed
  103. UTF8 buffer and thus can not return any data
  104. in DestinationUnicodeValue, then we will
  105. zero-fill the contents of DestinationUnicodeVal.
  106. Return Value:
  107. TRUE - We received a terminating character for our UTF8 buffer and will
  108. return a decoded Unicode value in DestinationUnicode.
  109. FALSE - We haven't yet received a terminating character for our UTF8
  110. buffer.
  111. --*/
  112. {
  113. // ULONG Count = 0;
  114. ULONG i = 0;
  115. BOOLEAN ReturnValue = FALSE;
  116. //
  117. // Insert our byte into ExistingUtf8Buffer.
  118. //
  119. i = 0;
  120. do {
  121. if( ExistingUtf8Buffer[i] == 0 ) {
  122. ExistingUtf8Buffer[i] = IncomingByte;
  123. break;
  124. }
  125. i++;
  126. } while( i < 3 );
  127. //
  128. // If we didn't get to actually insert our IncomingByte,
  129. // then someone sent us a fully-qualified UTF8 buffer.
  130. // This means we're about to drop IncomingByte.
  131. //
  132. // Drop the zero-th byte, shift everything over by one
  133. // and insert our new character.
  134. //
  135. // This implies that we should *never* need to zero out
  136. // the contents of ExistingUtf8Buffer unless we detect
  137. // a completed UTF8 packet. Otherwise, assume one of
  138. // these cases:
  139. // 1. We started listening mid-stream, so we caught the
  140. // last half of a UTF8 packet. In this case, we'll
  141. // end up shifting the contents of ExistingUtf8Buffer
  142. // until we detect a proper UTF8 start byte in the zero-th
  143. // position.
  144. // 2. We got some garbage character, which would invalidate
  145. // a UTF8 packet. By using the logic below, we would
  146. // end up disregarding that packet and waiting for
  147. // the next UTF8 packet to come in.
  148. if( i >= 3 ) {
  149. ExistingUtf8Buffer[0] = ExistingUtf8Buffer[1];
  150. ExistingUtf8Buffer[1] = ExistingUtf8Buffer[2];
  151. ExistingUtf8Buffer[2] = IncomingByte;
  152. }
  153. //
  154. // Attempt to convert the UTF8 buffer
  155. //
  156. // UTF8 decodes to Unicode in the following fashion:
  157. // If the high-order bit is 0 in the first byte:
  158. // 0xxxxxxx yyyyyyyy zzzzzzzz decodes to a Unicode value of 00000000 0xxxxxxx
  159. //
  160. // If the high-order 3 bits in the first byte == 6:
  161. // 110xxxxx 10yyyyyy zzzzzzzz decodes to a Unicode value of 00000xxx xxyyyyyy
  162. //
  163. // If the high-order 3 bits in the first byte == 7:
  164. // 1110xxxx 10yyyyyy 10zzzzzz decodes to a Unicode value of xxxxyyyy yyzzzzzz
  165. //
  166. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - About to decode the UTF8 buffer.\n" ));
  167. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, " UTF8[0]: 0x%02lx UTF8[1]: 0x%02lx UTF8[2]: 0x%02lx\n",
  168. ExistingUtf8Buffer[0],
  169. ExistingUtf8Buffer[1],
  170. ExistingUtf8Buffer[2] ));
  171. if( (ExistingUtf8Buffer[0] & 0x80) == 0 ) {
  172. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - Case1\n" ));
  173. //
  174. // First case described above. Just return the first byte
  175. // of our UTF8 buffer.
  176. //
  177. *DestinationUnicodeVal = (WCHAR)(ExistingUtf8Buffer[0]);
  178. //
  179. // We used 1 byte. Discard that byte and shift everything
  180. // in our buffer over by 1.
  181. //
  182. ExistingUtf8Buffer[0] = ExistingUtf8Buffer[1];
  183. ExistingUtf8Buffer[1] = ExistingUtf8Buffer[2];
  184. ExistingUtf8Buffer[2] = 0;
  185. ReturnValue = TRUE;
  186. } else if( (ExistingUtf8Buffer[0] & 0xE0) == 0xC0 ) {
  187. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - 1st byte of UTF8 buffer says Case2\n" ));
  188. //
  189. // Second case described above. Decode the first 2 bytes of
  190. // of our UTF8 buffer.
  191. //
  192. if( (ExistingUtf8Buffer[1] & 0xC0) == 0x80 ) {
  193. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - 2nd byte of UTF8 buffer says Case2.\n" ));
  194. // upper byte: 00000xxx
  195. *DestinationUnicodeVal = ((ExistingUtf8Buffer[0] >> 2) & 0x07);
  196. *DestinationUnicodeVal = *DestinationUnicodeVal << 8;
  197. // high bits of lower byte: xx000000
  198. *DestinationUnicodeVal |= ((ExistingUtf8Buffer[0] & 0x03) << 6);
  199. // low bits of lower byte: 00yyyyyy
  200. *DestinationUnicodeVal |= (ExistingUtf8Buffer[1] & 0x3F);
  201. //
  202. // We used 2 bytes. Discard those bytes and shift everything
  203. // in our buffer over by 2.
  204. //
  205. ExistingUtf8Buffer[0] = ExistingUtf8Buffer[2];
  206. ExistingUtf8Buffer[1] = 0;
  207. ExistingUtf8Buffer[2] = 0;
  208. ReturnValue = TRUE;
  209. }
  210. } else if( (ExistingUtf8Buffer[0] & 0xF0) == 0xE0 ) {
  211. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - 1st byte of UTF8 buffer says Case3\n" ));
  212. //
  213. // Third case described above. Decode the all 3 bytes of
  214. // of our UTF8 buffer.
  215. //
  216. if( (ExistingUtf8Buffer[1] & 0xC0) == 0x80 ) {
  217. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - 2nd byte of UTF8 buffer says Case3\n" ));
  218. if( (ExistingUtf8Buffer[2] & 0xC0) == 0x80 ) {
  219. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpTranslateUtf8ToUnicode - 3rd byte of UTF8 buffer says Case3\n" ));
  220. // upper byte: xxxx0000
  221. *DestinationUnicodeVal = ((ExistingUtf8Buffer[0] << 4) & 0xF0);
  222. // upper byte: 0000yyyy
  223. *DestinationUnicodeVal |= ((ExistingUtf8Buffer[1] >> 2) & 0x0F);
  224. *DestinationUnicodeVal = *DestinationUnicodeVal << 8;
  225. // lower byte: yy000000
  226. *DestinationUnicodeVal |= ((ExistingUtf8Buffer[1] << 6) & 0xC0);
  227. // lower byte: 00zzzzzz
  228. *DestinationUnicodeVal |= (ExistingUtf8Buffer[2] & 0x3F);
  229. //
  230. // We used all 3 bytes. Zero out the buffer.
  231. //
  232. ExistingUtf8Buffer[0] = 0;
  233. ExistingUtf8Buffer[1] = 0;
  234. ExistingUtf8Buffer[2] = 0;
  235. ReturnValue = TRUE;
  236. }
  237. }
  238. }
  239. return ReturnValue;
  240. }
  241. VOID
  242. SpTermInitialize(
  243. VOID
  244. )
  245. /*++
  246. Routine Description:
  247. Attempts to connect to a VT100 attached to COM1
  248. Arguments:
  249. None.
  250. Return Value:
  251. None.
  252. --*/
  253. {
  254. HEADLESS_CMD_ENABLE_TERMINAL Command;
  255. NTSTATUS Status;
  256. Command.Enable = TRUE;
  257. Status = HeadlessDispatch(HeadlessCmdEnableTerminal,
  258. &Command,
  259. sizeof(HEADLESS_CMD_ENABLE_TERMINAL),
  260. NULL,
  261. NULL
  262. );
  263. HeadlessTerminalConnected = NT_SUCCESS(Status);
  264. }
  265. VOID
  266. SpTermDisplayStringOnTerminal(
  267. IN PWSTR String,
  268. IN UCHAR Attribute,
  269. IN ULONG X, // 0-based coordinates (character units)
  270. IN ULONG Y
  271. )
  272. /*++
  273. Routine Description:
  274. Write a string of characters to the terminal.
  275. Arguments:
  276. Character - supplies a string to be displayed at the given position.
  277. Attribute - supplies the attributes for the characters in the string.
  278. X,Y - specify the character-based (0-based) position of the output.
  279. Return Value:
  280. None.
  281. --*/
  282. {
  283. PWSTR EscapeString;
  284. //
  285. // send <CSI>x;yH to move the cursor to the specified location
  286. //
  287. swprintf(UnicodeScratchBuffer, L"\033[%d;%dH", Y + 1, X + 1);
  288. SpTermSendStringToTerminal(UnicodeScratchBuffer, TRUE);
  289. //
  290. // convert any attributes to an escape string. EscapeString uses
  291. // the TerminalBuffer global scratch buffer
  292. //
  293. EscapeString = SpTermAttributeToTerminalEscapeString(Attribute);
  294. //
  295. // transmit the escape string if we received one
  296. //
  297. if (EscapeString != NULL) {
  298. SpTermSendStringToTerminal(EscapeString, TRUE);
  299. }
  300. //
  301. // finally send the actual string contents to the terminal
  302. //
  303. SpTermSendStringToTerminal(String, FALSE);
  304. }
  305. PWSTR
  306. SpTermAttributeToTerminalEscapeString(
  307. IN UCHAR Attribute
  308. )
  309. /*++
  310. Routine Description:
  311. Convert a vga attribute byte to an escape sequence to send to the terminal.
  312. Arguments:
  313. Attribute - supplies the attribute.
  314. Return Value:
  315. A pointer to the escape sequence, or NULL if it could not be converted.
  316. --*/
  317. {
  318. ULONG BgColor;
  319. ULONG FgColor;
  320. BOOLEAN Inverse;
  321. BgColor = (Attribute & 0x70) >> 4;
  322. FgColor = Attribute & 0x07;
  323. Inverse = !((BgColor == 0) || (BgColor == DEFAULT_BACKGROUND));
  324. //
  325. // Convert the colors.
  326. //
  327. switch (BgColor) {
  328. case ATT_BLUE:
  329. BgColor = 44;
  330. break;
  331. case ATT_GREEN:
  332. BgColor = 42;
  333. break;
  334. case ATT_CYAN:
  335. BgColor = 46;
  336. break;
  337. case ATT_RED:
  338. BgColor = 41;
  339. break;
  340. case ATT_MAGENTA:
  341. BgColor = 45;
  342. break;
  343. case ATT_YELLOW:
  344. BgColor = 43;
  345. break;
  346. case ATT_BLACK:
  347. BgColor = 40;
  348. break;
  349. case ATT_WHITE:
  350. BgColor = 47;
  351. break;
  352. }
  353. switch (FgColor) {
  354. case ATT_BLUE:
  355. FgColor = 34;
  356. break;
  357. case ATT_GREEN:
  358. FgColor = 32;
  359. break;
  360. case ATT_CYAN:
  361. FgColor = 36;
  362. break;
  363. case ATT_RED:
  364. FgColor = 31;
  365. break;
  366. case ATT_MAGENTA:
  367. FgColor = 35;
  368. break;
  369. case ATT_YELLOW:
  370. FgColor = 33;
  371. break;
  372. case ATT_BLACK:
  373. FgColor = 30;
  374. break;
  375. case ATT_WHITE:
  376. FgColor = 37;
  377. break;
  378. }
  379. //
  380. // <CSI>%1;%2;%3m is the escape to set a color
  381. // where 1 = video mode
  382. // 2 = foreground color
  383. // 3 = background color
  384. //
  385. swprintf(UnicodeScratchBuffer,
  386. L"\033[%u;%u;%um",
  387. (Inverse ? 7 : 0),
  388. FgColor,
  389. BgColor
  390. );
  391. return UnicodeScratchBuffer;
  392. }
  393. VOID
  394. SpTermSendStringToTerminal(
  395. IN PWSTR String,
  396. IN BOOLEAN Raw
  397. )
  398. /*++
  399. Routine Description:
  400. Write a character string to the terminal, translating some codes if desired.
  401. Arguments:
  402. String - NULL terminated string to write.
  403. Raw - Send the string raw or not.
  404. Return Value:
  405. None.
  406. --*/
  407. {
  408. ULONG i = 0;
  409. PWSTR LocalBuffer = UnicodeScratchBuffer;
  410. //
  411. // if we're in an FE build, we do UTF8, otherwise we do oem codepage
  412. //
  413. BOOL DoUtf8 = (VideoFunctionVector != &VgaVideoVector);
  414. ASSERT(FIELD_OFFSET(HEADLESS_CMD_PUT_STRING, String) == 0); // ASSERT if anyone changes this structure.
  415. //
  416. // Don't do anything if we aren't running headless.
  417. //
  418. if( !HeadlessTerminalConnected ) {
  419. return;
  420. }
  421. if (Raw) {
  422. if (DoUtf8) {
  423. SpTranslateUnicodeToUtf8( String, Utf8ConversionBuffer );
  424. HeadlessDispatch( HeadlessCmdPutData,
  425. Utf8ConversionBuffer,
  426. strlen(Utf8ConversionBuffer),
  427. NULL,
  428. NULL
  429. );
  430. } else {
  431. //
  432. // Convert unicode string to oem, guarding against overflow.
  433. //
  434. RtlUnicodeToOemN(
  435. Utf8ConversionBuffer,
  436. sizeof(Utf8ConversionBuffer)-1, // guarantee room for nul
  437. NULL,
  438. String,
  439. (wcslen(String)+1)*sizeof(WCHAR)
  440. );
  441. Utf8ConversionBuffer[sizeof(Utf8ConversionBuffer)-1] = '\0';
  442. HeadlessDispatch( HeadlessCmdPutString,
  443. Utf8ConversionBuffer,
  444. strlen(Utf8ConversionBuffer) + sizeof('\0'),
  445. NULL,
  446. NULL
  447. );
  448. }
  449. return;
  450. }
  451. while (*String != L'\0') {
  452. LocalBuffer[i++] = *String;
  453. if (*String == L'\n') {
  454. //
  455. // Every \n becomes a \n\r sequence.
  456. //
  457. LocalBuffer[i++] = L'\r';
  458. } else if (*String == 0x00DC) {
  459. //
  460. // The cursor becomes a space and then a backspace, this is to
  461. // delete the old character and position the terminal cursor properly.
  462. //
  463. LocalBuffer[i-1] = 0x0020;
  464. LocalBuffer[i++] = 0x0008;
  465. }
  466. //
  467. // we've got an entire line of text -- we need to transmit it now or
  468. // we can end up scrolling the text and everything will look funny from
  469. // this point forward.
  470. //
  471. if (i >= 70) {
  472. LocalBuffer[i] = L'\0';
  473. if (DoUtf8) {
  474. SpTranslateUnicodeToUtf8( LocalBuffer, Utf8ConversionBuffer );
  475. HeadlessDispatch(HeadlessCmdPutData,
  476. Utf8ConversionBuffer,
  477. strlen(Utf8ConversionBuffer),
  478. NULL,
  479. NULL
  480. );
  481. } else {
  482. //
  483. // Convert unicode string to oem, guarding against overflow.
  484. //
  485. RtlUnicodeToOemN(
  486. Utf8ConversionBuffer,
  487. sizeof(Utf8ConversionBuffer)-1, // guarantee room for nul
  488. NULL,
  489. LocalBuffer,
  490. (wcslen(LocalBuffer)+1)*sizeof(WCHAR)
  491. );
  492. Utf8ConversionBuffer[sizeof(Utf8ConversionBuffer)-1] = '\0';
  493. HeadlessDispatch(HeadlessCmdPutString,
  494. Utf8ConversionBuffer,
  495. strlen(Utf8ConversionBuffer) + sizeof('\0'),
  496. NULL,
  497. NULL
  498. );
  499. }
  500. i = 0;
  501. }
  502. String++;
  503. }
  504. LocalBuffer[i] = L'\0';
  505. if (DoUtf8) {
  506. SpTranslateUnicodeToUtf8( LocalBuffer, Utf8ConversionBuffer );
  507. HeadlessDispatch(HeadlessCmdPutData,
  508. Utf8ConversionBuffer,
  509. strlen(Utf8ConversionBuffer),
  510. NULL,
  511. NULL
  512. );
  513. } else {
  514. //
  515. // Convert unicode string to oem, guarding against overflow.
  516. //
  517. RtlUnicodeToOemN(
  518. Utf8ConversionBuffer,
  519. sizeof(Utf8ConversionBuffer)-1, // guarantee room for nul
  520. NULL,
  521. LocalBuffer,
  522. (wcslen(LocalBuffer)+1)*sizeof(WCHAR)
  523. );
  524. Utf8ConversionBuffer[sizeof(Utf8ConversionBuffer)-1] = '\0';
  525. HeadlessDispatch(HeadlessCmdPutString,
  526. Utf8ConversionBuffer,
  527. strlen(Utf8ConversionBuffer) + sizeof('\0'),
  528. NULL,
  529. NULL
  530. );
  531. }
  532. }
  533. VOID
  534. SpTermTerminate(
  535. VOID
  536. )
  537. /*++
  538. Routine Description:
  539. Close down connection to the dumb terminal
  540. Arguments:
  541. None.
  542. Return Value:
  543. None.
  544. --*/
  545. {
  546. HEADLESS_CMD_ENABLE_TERMINAL Command;
  547. //
  548. // Don't do anything if we aren't running headless.
  549. //
  550. if( !HeadlessTerminalConnected ) {
  551. return;
  552. }
  553. Command.Enable = FALSE;
  554. HeadlessDispatch(HeadlessCmdEnableTerminal,
  555. &Command,
  556. sizeof(HEADLESS_CMD_ENABLE_TERMINAL),
  557. NULL,
  558. NULL
  559. );
  560. HeadlessTerminalConnected = FALSE;
  561. }
  562. BOOLEAN
  563. SpTermIsKeyWaiting(
  564. VOID
  565. )
  566. /*++
  567. Routine Description:
  568. Probe for a read.
  569. Arguments:
  570. None.
  571. Return Value:
  572. TRUE if there is a character waiting for input, else FALSE.
  573. --*/
  574. {
  575. HEADLESS_RSP_POLL Response;
  576. NTSTATUS Status;
  577. SIZE_T Length;
  578. //
  579. // Don't do anything if we aren't running headless.
  580. //
  581. if( !HeadlessTerminalConnected ) {
  582. return FALSE;
  583. }
  584. Length = sizeof(HEADLESS_RSP_POLL);
  585. Response.QueuedInput = FALSE;
  586. Status = HeadlessDispatch(HeadlessCmdTerminalPoll,
  587. NULL,
  588. 0,
  589. &Response,
  590. &Length
  591. );
  592. return (NT_SUCCESS(Status) && Response.QueuedInput);
  593. }
  594. ULONG
  595. SpTermGetKeypress(
  596. VOID
  597. )
  598. /*++
  599. Routine Description:
  600. Read in a (possible) sequence of keystrokes and return a Key value.
  601. Arguments:
  602. None.
  603. Return Value:
  604. 0 if no key is waiting, else a ULONG key value.
  605. --*/
  606. {
  607. UCHAR Byte;
  608. BOOLEAN Success;
  609. TIME_FIELDS StartTime;
  610. TIME_FIELDS EndTime;
  611. HEADLESS_RSP_GET_BYTE Response;
  612. SIZE_T Length;
  613. NTSTATUS Status;
  614. //
  615. // Don't do anything if we aren't running headless.
  616. //
  617. if( !HeadlessTerminalConnected ) {
  618. return 0;
  619. }
  620. //
  621. // Read first character
  622. //
  623. Length = sizeof(HEADLESS_RSP_GET_BYTE);
  624. Status = HeadlessDispatch(HeadlessCmdGetByte,
  625. NULL,
  626. 0,
  627. &Response,
  628. &Length
  629. );
  630. if (NT_SUCCESS(Status)) {
  631. Byte = Response.Value;
  632. } else {
  633. Byte = 0;
  634. }
  635. //
  636. // Handle all the special escape codes.
  637. //
  638. if (Byte == 0x8) { // backspace (^h)
  639. return ASCI_BS;
  640. }
  641. if (Byte == 0x7F) { // delete
  642. return KEY_DELETE;
  643. }
  644. if ((Byte == '\r') || (Byte == '\n')) { // return
  645. return ASCI_CR;
  646. }
  647. if (Byte == 0x1b) { // Escape key
  648. do {
  649. Success = HalQueryRealTimeClock(&StartTime);
  650. ASSERT(Success);
  651. //
  652. // Adjust StartTime to be our ending time.
  653. //
  654. StartTime.Second += 2;
  655. if (StartTime.Second > 59) {
  656. StartTime.Second -= 60;
  657. }
  658. while (!SpTermIsKeyWaiting()) {
  659. //
  660. // Give the user 1 second to type in a follow up key.
  661. //
  662. Success = HalQueryRealTimeClock(&EndTime);
  663. ASSERT(Success);
  664. if (StartTime.Second == EndTime.Second) {
  665. break;
  666. }
  667. }
  668. if (!SpTermIsKeyWaiting()) {
  669. return ASCI_ESC;
  670. }
  671. //
  672. // Read the next keystroke
  673. //
  674. Length = sizeof(HEADLESS_RSP_GET_BYTE);
  675. Status = HeadlessDispatch(HeadlessCmdGetByte,
  676. NULL,
  677. 0,
  678. &Response,
  679. &Length
  680. );
  681. if (NT_SUCCESS(Status)) {
  682. Byte = Response.Value;
  683. } else {
  684. Byte = 0;
  685. }
  686. //
  687. // Some terminals send ESC, or ESC-[ to mean
  688. // they're about to send a control sequence. We've already
  689. // gotten an ESC key, so ignore an '[' if it comes in.
  690. //
  691. } while ( Byte == '[' );
  692. switch (Byte) {
  693. case '@':
  694. return KEY_F12;
  695. case '!':
  696. return KEY_F11;
  697. case '0':
  698. return KEY_F10;
  699. case '9':
  700. return KEY_F9;
  701. case '8':
  702. return KEY_F8;
  703. case '7':
  704. return KEY_F7;
  705. case '6':
  706. return KEY_F6;
  707. case '5':
  708. return KEY_F5;
  709. case '4':
  710. return KEY_F4;
  711. case '3':
  712. return KEY_F3;
  713. case '2':
  714. return KEY_F2;
  715. case '1':
  716. return KEY_F1;
  717. case '+':
  718. return KEY_INSERT;
  719. case '-':
  720. return KEY_DELETE;
  721. case 'H':
  722. return KEY_HOME;
  723. case 'K':
  724. return KEY_END;
  725. case '?':
  726. return KEY_PAGEUP;
  727. case '/':
  728. return KEY_PAGEDOWN;
  729. case 'A':
  730. return KEY_UP;
  731. case 'B':
  732. return KEY_DOWN;
  733. case 'C':
  734. return KEY_RIGHT;
  735. case 'D':
  736. return KEY_LEFT;
  737. }
  738. //
  739. // We didn't get anything we recognized after the
  740. // ESC key. Just return the ESC key.
  741. //
  742. return ASCI_ESC;
  743. } // Escape key
  744. //
  745. // The incoming byte isn't an escape code.
  746. //
  747. // Decode it as if it's a UTF8 stream.
  748. //
  749. if( SpTranslateUtf8ToUnicode( Byte,
  750. IncomingUtf8ConversionBuffer,
  751. &IncomingUnicodeValue ) ) {
  752. //
  753. // He returned TRUE, so we must have recieved a complete
  754. // UTF8-encoded character.
  755. //
  756. return IncomingUnicodeValue;
  757. } else {
  758. //
  759. // The UTF8 stream isn't complete yet, so we don't have
  760. // a decoded character to return yet.
  761. //
  762. return 0;
  763. }
  764. }
  765. VOID
  766. SpTermDrain(
  767. VOID
  768. )
  769. /*++
  770. Routine Description:
  771. Read in and throw out all characters in input stream
  772. Arguments:
  773. None.
  774. Return Value:
  775. None.
  776. --*/
  777. {
  778. while (SpTermIsKeyWaiting()) {
  779. SpTermGetKeypress();
  780. }
  781. }