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.

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