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.

4374 lines
139 KiB

  1. /*++
  2. Copyright (c) 1985 - 1999, Microsoft Corporation
  3. Module Name:
  4. input.c
  5. Abstract:
  6. This file implements the circular buffer management for
  7. input events.
  8. The circular buffer is described by a header,
  9. which resides in the beginning of the memory allocated when the
  10. buffer is created. The header contains all of the
  11. per-buffer information, such as reader, writer, and
  12. reference counts, and also holds the pointers into
  13. the circular buffer proper.
  14. When the in and out pointers are equal, the circular buffer
  15. is empty. When the in pointer trails the out pointer
  16. by 1, the buffer is full. Thus, a 512 byte buffer can hold
  17. only 511 bytes; one byte is lost so that full and empty
  18. conditions can be distinguished. So that the user can
  19. put 512 bytes in a buffer that they created with a size
  20. of 512, we allow for this byte lost when allocating
  21. the memory.
  22. Author:
  23. Therese Stowell (thereses) 6-Nov-1990
  24. Adapted from OS/2 subsystem server\srvpipe.c
  25. Revision History:
  26. --*/
  27. #include "precomp.h"
  28. #pragma hdrstop
  29. #define CTRL_BUT_NOT_ALT(n) \
  30. (((n) & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) && \
  31. !((n) & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)))
  32. UINT ProgmanHandleMessage;
  33. int DialogBoxCount;
  34. LPTHREAD_START_ROUTINE CtrlRoutine; // address of client side ctrl-thread routine
  35. DWORD InputThreadTlsIndex;
  36. #define MAX_CHARS_FROM_1_KEYSTROKE 6
  37. //
  38. // the following data structures are a hack to work around the fact that
  39. // MapVirtualKey does not return the correct virtual key code in many cases.
  40. // we store the correct info (from the keydown message) in the CONSOLE_KEY_INFO
  41. // structure when a keydown message is translated. then when we receive a
  42. // wm_[sys][dead]char message, we retrieve it and clear out the record.
  43. //
  44. #define CONSOLE_FREE_KEY_INFO 0
  45. #define CONSOLE_MAX_KEY_INFO 32
  46. typedef struct _CONSOLE_KEY_INFO {
  47. HWND hWnd;
  48. WORD wVirtualKeyCode;
  49. WORD wVirtualScanCode;
  50. } CONSOLE_KEY_INFO, *PCONSOLE_KEY_INFO;
  51. CONSOLE_KEY_INFO ConsoleKeyInfo[CONSOLE_MAX_KEY_INFO];
  52. VOID
  53. UserExitWorkerThread(NTSTATUS Status);
  54. BOOL
  55. InitWindowClass( VOID );
  56. #if !defined(FE_SB)
  57. NTSTATUS
  58. ReadBuffer(
  59. IN PINPUT_INFORMATION InputInformation,
  60. OUT PVOID Buffer,
  61. IN ULONG Length,
  62. OUT PULONG EventsRead,
  63. IN BOOL Peek,
  64. IN BOOL StreamRead,
  65. OUT PBOOL ResetWaitEvent
  66. );
  67. #endif
  68. NTSTATUS
  69. CreateInputBuffer(
  70. IN ULONG NumberOfEvents OPTIONAL,
  71. IN PINPUT_INFORMATION InputBufferInformation
  72. #if defined(FE_SB)
  73. ,
  74. IN PCONSOLE_INFORMATION Console
  75. #endif
  76. )
  77. /*++
  78. Routine Description:
  79. This routine creates an input buffer. It allocates the circular
  80. buffer and initializes the information fields.
  81. Arguments:
  82. NumberOfEvents - Size of input buffer in events.
  83. InputBufferInformation - Pointer to input buffer information structure.
  84. Return Value:
  85. --*/
  86. {
  87. ULONG BufferSize;
  88. NTSTATUS Status;
  89. if (NumberOfEvents == 0) {
  90. NumberOfEvents = DEFAULT_NUMBER_OF_EVENTS;
  91. }
  92. // allocate memory for circular buffer
  93. BufferSize = sizeof(INPUT_RECORD) * (NumberOfEvents+1);
  94. InputBufferInformation->InputBuffer = ConsoleHeapAlloc(BUFFER_TAG, BufferSize);
  95. if (InputBufferInformation->InputBuffer == NULL) {
  96. return STATUS_NO_MEMORY;
  97. }
  98. Status = NtCreateEvent(&InputBufferInformation->InputWaitEvent,
  99. EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
  100. if (!NT_SUCCESS(Status)) {
  101. ConsoleHeapFree(InputBufferInformation->InputBuffer);
  102. return STATUS_NO_MEMORY;
  103. }
  104. InitializeListHead(&InputBufferInformation->ReadWaitQueue);
  105. // initialize buffer header
  106. InputBufferInformation->InputBufferSize = NumberOfEvents;
  107. InputBufferInformation->ShareAccess.OpenCount = 0;
  108. InputBufferInformation->ShareAccess.Readers = 0;
  109. InputBufferInformation->ShareAccess.Writers = 0;
  110. InputBufferInformation->ShareAccess.SharedRead = 0;
  111. InputBufferInformation->ShareAccess.SharedWrite = 0;
  112. InputBufferInformation->InputMode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
  113. InputBufferInformation->RefCount = 0;
  114. InputBufferInformation->First = (ULONG_PTR) InputBufferInformation->InputBuffer;
  115. InputBufferInformation->In = (ULONG_PTR) InputBufferInformation->InputBuffer;
  116. InputBufferInformation->Out = (ULONG_PTR) InputBufferInformation->InputBuffer;
  117. InputBufferInformation->Last = (ULONG_PTR) InputBufferInformation->InputBuffer + BufferSize;
  118. #if defined(FE_SB)
  119. #if defined(FE_IME)
  120. InputBufferInformation->ImeMode.Disable = FALSE;
  121. InputBufferInformation->ImeMode.Unavailable = FALSE;
  122. InputBufferInformation->ImeMode.Open = FALSE;
  123. InputBufferInformation->ImeMode.ReadyConversion = FALSE;
  124. #endif // FE_IME
  125. InputBufferInformation->Console = Console;
  126. RtlZeroMemory(&InputBufferInformation->ReadConInpDbcsLeadByte,sizeof(INPUT_RECORD));
  127. RtlZeroMemory(&InputBufferInformation->WriteConInpDbcsLeadByte,sizeof(INPUT_RECORD));
  128. #endif
  129. return STATUS_SUCCESS;
  130. }
  131. NTSTATUS
  132. ReinitializeInputBuffer(
  133. OUT PINPUT_INFORMATION InputBufferInformation
  134. )
  135. /*++
  136. Routine Description:
  137. This routine resets the input buffer information fields to their
  138. initial values.
  139. Arguments:
  140. InputBufferInformation - Pointer to input buffer information structure.
  141. Return Value:
  142. Note:
  143. The console lock must be held when calling this routine.
  144. --*/
  145. {
  146. NtClearEvent(InputBufferInformation->InputWaitEvent);
  147. InputBufferInformation->ShareAccess.OpenCount = 0;
  148. InputBufferInformation->ShareAccess.Readers = 0;
  149. InputBufferInformation->ShareAccess.Writers = 0;
  150. InputBufferInformation->ShareAccess.SharedRead = 0;
  151. InputBufferInformation->ShareAccess.SharedWrite = 0;
  152. InputBufferInformation->InputMode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
  153. InputBufferInformation->In = (ULONG_PTR) InputBufferInformation->InputBuffer;
  154. InputBufferInformation->Out = (ULONG_PTR) InputBufferInformation->InputBuffer;
  155. return STATUS_SUCCESS;
  156. }
  157. VOID
  158. FreeInputBuffer(
  159. IN PINPUT_INFORMATION InputBufferInformation
  160. )
  161. /*++
  162. Routine Description:
  163. This routine frees the resources associated with an input buffer.
  164. Arguments:
  165. InputBufferInformation - Pointer to input buffer information structure.
  166. Return Value:
  167. --*/
  168. {
  169. UserAssert(InputBufferInformation->RefCount == 0);
  170. CloseHandle(InputBufferInformation->InputWaitEvent);
  171. ConsoleHeapFree(InputBufferInformation->InputBuffer);
  172. }
  173. NTSTATUS
  174. WaitForMoreToRead(
  175. IN PINPUT_INFORMATION InputInformation,
  176. IN PCSR_API_MSG Message OPTIONAL,
  177. IN CSR_WAIT_ROUTINE WaitRoutine OPTIONAL,
  178. IN PVOID WaitParameter OPTIONAL,
  179. IN ULONG WaitParameterLength OPTIONAL,
  180. IN BOOLEAN WaitBlockExists OPTIONAL
  181. )
  182. /*++
  183. Routine Description:
  184. This routine waits for a writer to add data to the buffer.
  185. Arguments:
  186. InputInformation - buffer to wait for
  187. Console - Pointer to console buffer information.
  188. Message - if called from dll (not InputThread), points to api
  189. message. this parameter is used for wait block processing.
  190. WaitRoutine - Routine to call when wait is woken up.
  191. WaitParameter - Parameter to pass to wait routine.
  192. WaitParameterLength - Length of wait parameter.
  193. WaitBlockExists - TRUE if wait block has already been created.
  194. Return Value:
  195. STATUS_WAIT - call was from client and wait block has been created.
  196. STATUS_SUCCESS - call was from server and wait has been satisfied.
  197. --*/
  198. {
  199. PVOID WaitParameterBuffer;
  200. if (!WaitBlockExists) {
  201. WaitParameterBuffer = ConsoleHeapAlloc(WAIT_TAG, WaitParameterLength);
  202. if (WaitParameterBuffer == NULL) {
  203. return STATUS_NO_MEMORY;
  204. }
  205. RtlCopyMemory(WaitParameterBuffer,WaitParameter,WaitParameterLength);
  206. #if defined(FE_SB)
  207. if (WaitParameterLength == sizeof(COOKED_READ_DATA) &&
  208. InputInformation->Console->lpCookedReadData == WaitParameter) {
  209. InputInformation->Console->lpCookedReadData = WaitParameterBuffer;
  210. }
  211. #endif
  212. if (!CsrCreateWait(&InputInformation->ReadWaitQueue,
  213. WaitRoutine,
  214. CSR_SERVER_QUERYCLIENTTHREAD(),
  215. Message,
  216. WaitParameterBuffer)) {
  217. ConsoleHeapFree(WaitParameterBuffer);
  218. #if defined(FE_SB)
  219. InputInformation->Console->lpCookedReadData = NULL;
  220. #endif
  221. return STATUS_NO_MEMORY;
  222. }
  223. }
  224. return CONSOLE_STATUS_WAIT;
  225. }
  226. VOID
  227. WakeUpReadersWaitingForData(
  228. IN PCONSOLE_INFORMATION Console,
  229. PINPUT_INFORMATION InputInformation
  230. )
  231. /*++
  232. Routine Description:
  233. This routine wakes up readers waiting for data to read.
  234. Arguments:
  235. InputInformation - buffer to alert readers for
  236. Return Value:
  237. TRUE - The operation was successful
  238. FALSE/NULL - The operation failed.
  239. --*/
  240. {
  241. BOOLEAN WaitSatisfied;
  242. WaitSatisfied = CsrNotifyWait(&InputInformation->ReadWaitQueue,
  243. FALSE,
  244. NULL,
  245. NULL
  246. );
  247. if (WaitSatisfied) {
  248. // #334370 under stress, WaitQueue may already hold the satisfied waits
  249. UserAssert((Console->WaitQueue == NULL) ||
  250. (Console->WaitQueue == &InputInformation->ReadWaitQueue));
  251. Console->WaitQueue = &InputInformation->ReadWaitQueue;
  252. }
  253. }
  254. NTSTATUS
  255. GetNumberOfReadyEvents(
  256. IN PINPUT_INFORMATION InputInformation,
  257. OUT PULONG NumberOfEvents
  258. )
  259. /*++
  260. Routine Description:
  261. This routine returns the number of events in the input buffer.
  262. Arguments:
  263. InputInformation - Pointer to input buffer information structure.
  264. NumberOfEvents - On output contains the number of events.
  265. Return Value:
  266. Note:
  267. The console lock must be held when calling this routine.
  268. --*/
  269. {
  270. if (InputInformation->In < InputInformation->Out) {
  271. *NumberOfEvents = (ULONG)(InputInformation->Last - InputInformation->Out);
  272. *NumberOfEvents += (ULONG)(InputInformation->In - InputInformation->First);
  273. }
  274. else {
  275. *NumberOfEvents = (ULONG)(InputInformation->In - InputInformation->Out);
  276. }
  277. *NumberOfEvents /= sizeof(INPUT_RECORD);
  278. return STATUS_SUCCESS;
  279. }
  280. NTSTATUS
  281. FlushAllButKeys(
  282. PINPUT_INFORMATION InputInformation
  283. )
  284. /*++
  285. Routine Description:
  286. This routine removes all but the key events from the buffer.
  287. Arguments:
  288. InputInformation - Pointer to input buffer information structure.
  289. Return Value:
  290. Note:
  291. The console lock must be held when calling this routine.
  292. --*/
  293. {
  294. ULONG NumberOfEventsRead,i;
  295. NTSTATUS Status;
  296. PINPUT_RECORD TmpInputBuffer,InPtr,TmpInputBufferPtr;
  297. ULONG BufferSize;
  298. BOOL Dummy;
  299. if (InputInformation->In != InputInformation->Out) {
  300. //
  301. // allocate memory for temp buffer
  302. //
  303. BufferSize = sizeof(INPUT_RECORD) * (InputInformation->InputBufferSize+1);
  304. TmpInputBuffer = ConsoleHeapAlloc(TMP_TAG, BufferSize);
  305. if (TmpInputBuffer == NULL) {
  306. return STATUS_NO_MEMORY;
  307. }
  308. TmpInputBufferPtr = TmpInputBuffer;
  309. //
  310. // copy input buffer.
  311. // let ReadBuffer do any compaction work.
  312. //
  313. Status = ReadBuffer(InputInformation,
  314. TmpInputBuffer,
  315. InputInformation->InputBufferSize,
  316. &NumberOfEventsRead,
  317. TRUE,
  318. FALSE,
  319. &Dummy
  320. #if defined(FE_SB)
  321. ,
  322. TRUE
  323. #endif
  324. );
  325. if (!NT_SUCCESS(Status)) {
  326. ConsoleHeapFree(TmpInputBuffer);
  327. return Status;
  328. }
  329. InputInformation->Out = (ULONG_PTR) InputInformation->InputBuffer;
  330. InPtr = InputInformation->InputBuffer;
  331. for (i=0;i<NumberOfEventsRead;i++) {
  332. if (TmpInputBuffer->EventType == KEY_EVENT) {
  333. *InPtr = *TmpInputBuffer;
  334. InPtr++;
  335. }
  336. TmpInputBuffer++;
  337. }
  338. InputInformation->In = (ULONG_PTR) InPtr;
  339. if (InputInformation->In == InputInformation->Out) {
  340. NtClearEvent(InputInformation->InputWaitEvent);
  341. }
  342. ConsoleHeapFree(TmpInputBufferPtr);
  343. }
  344. return STATUS_SUCCESS;
  345. }
  346. NTSTATUS
  347. FlushInputBuffer(
  348. PINPUT_INFORMATION InputInformation
  349. )
  350. /*++
  351. Routine Description:
  352. This routine empties the input buffer
  353. Arguments:
  354. InputInformation - Pointer to input buffer information structure.
  355. Return Value:
  356. Note:
  357. The console lock must be held when calling this routine.
  358. --*/
  359. {
  360. InputInformation->In = (ULONG_PTR) InputInformation->InputBuffer;
  361. InputInformation->Out = (ULONG_PTR) InputInformation->InputBuffer;
  362. NtClearEvent(InputInformation->InputWaitEvent);
  363. return STATUS_SUCCESS;
  364. }
  365. NTSTATUS
  366. SetInputBufferSize(
  367. IN PINPUT_INFORMATION InputInformation,
  368. IN ULONG Size
  369. )
  370. /*++
  371. Routine Description:
  372. This routine resizes the input buffer.
  373. Arguments:
  374. InputInformation - Pointer to input buffer information structure.
  375. Size - New size in number of events.
  376. Return Value:
  377. Note:
  378. The console lock must be held when calling this routine.
  379. --*/
  380. {
  381. ULONG NumberOfEventsRead;
  382. NTSTATUS Status;
  383. PINPUT_RECORD InputBuffer;
  384. ULONG BufferSize;
  385. BOOL Dummy;
  386. #if DBG
  387. ULONG_PTR NumberOfEvents;
  388. if (InputInformation->In < InputInformation->Out) {
  389. NumberOfEvents = InputInformation->Last - InputInformation->Out;
  390. NumberOfEvents += InputInformation->In - InputInformation->First;
  391. } else {
  392. NumberOfEvents = InputInformation->In - InputInformation->Out;
  393. }
  394. NumberOfEvents /= sizeof(INPUT_RECORD);
  395. #endif
  396. UserAssert(Size > InputInformation->InputBufferSize);
  397. //
  398. // Allocate memory for new input buffer.
  399. //
  400. BufferSize = sizeof(INPUT_RECORD) * (Size+1);
  401. InputBuffer = ConsoleHeapAlloc(BUFFER_TAG, BufferSize);
  402. if (InputBuffer == NULL) {
  403. return STATUS_NO_MEMORY;
  404. }
  405. //
  406. // Copy old input buffer. Let the ReadBuffer do any compaction work.
  407. //
  408. Status = ReadBuffer(InputInformation,
  409. InputBuffer,
  410. Size,
  411. &NumberOfEventsRead,
  412. TRUE,
  413. FALSE,
  414. &Dummy
  415. #if defined(FE_SB)
  416. ,
  417. TRUE
  418. #endif
  419. );
  420. if (!NT_SUCCESS(Status)) {
  421. ConsoleHeapFree(InputBuffer);
  422. return Status;
  423. }
  424. InputInformation->Out = (ULONG_PTR)InputBuffer;
  425. InputInformation->In = (ULONG_PTR)InputBuffer + sizeof(INPUT_RECORD) * NumberOfEventsRead;
  426. //
  427. // adjust pointers
  428. //
  429. InputInformation->First = (ULONG_PTR) InputBuffer;
  430. InputInformation->Last = (ULONG_PTR) InputBuffer + BufferSize;
  431. //
  432. // free old input buffer
  433. //
  434. ConsoleHeapFree(InputInformation->InputBuffer);
  435. InputInformation->InputBufferSize = Size;
  436. InputInformation->InputBuffer = InputBuffer;
  437. return Status;
  438. }
  439. NTSTATUS
  440. ReadBuffer(
  441. IN PINPUT_INFORMATION InputInformation,
  442. OUT PVOID Buffer,
  443. IN ULONG Length,
  444. OUT PULONG EventsRead,
  445. IN BOOL Peek,
  446. IN BOOL StreamRead,
  447. OUT PBOOL ResetWaitEvent
  448. #ifdef FE_SB
  449. , IN BOOLEAN Unicode
  450. #endif
  451. )
  452. /*++
  453. Routine Description:
  454. This routine reads from a buffer. It does the actual circular buffer
  455. manipulation.
  456. Arguments:
  457. InputInformation - buffer to read from
  458. Buffer - buffer to read into
  459. Length - length of buffer in events
  460. EventsRead - where to store number of events read
  461. Peek - if TRUE, don't remove data from buffer, just copy it.
  462. StreamRead - if TRUE, events with repeat counts > 1 are returned
  463. as multiple events. also, EventsRead == 1.
  464. ResetWaitEvent - on exit, TRUE if buffer became empty.
  465. Return Value:
  466. ??
  467. Note:
  468. The console lock must be held when calling this routine.
  469. --*/
  470. {
  471. ULONG TransferLength,OldTransferLength;
  472. ULONG BufferLengthInBytes;
  473. #ifdef FE_SB
  474. PCONSOLE_INFORMATION Console;
  475. ULONG Length2;
  476. PINPUT_RECORD BufferRecords;
  477. PINPUT_RECORD QueueRecords;
  478. WCHAR UniChar;
  479. WORD EventType;
  480. #endif
  481. #ifdef FE_SB
  482. Console = InputInformation->Console;
  483. #endif
  484. *ResetWaitEvent = FALSE;
  485. //
  486. // if StreamRead, just return one record. if repeat count is greater
  487. // than one, just decrement it. the repeat count is > 1 if more than
  488. // one event of the same type was merged. we need to expand them back
  489. // to individual events here.
  490. //
  491. if (StreamRead &&
  492. ((PINPUT_RECORD)(InputInformation->Out))->EventType == KEY_EVENT) {
  493. UserAssert(Length == 1);
  494. UserAssert(InputInformation->In != InputInformation->Out);
  495. RtlMoveMemory((PBYTE)Buffer,
  496. (PBYTE)InputInformation->Out,
  497. sizeof(INPUT_RECORD)
  498. );
  499. InputInformation->Out += sizeof(INPUT_RECORD);
  500. if (InputInformation->Last == InputInformation->Out) {
  501. InputInformation->Out = InputInformation->First;
  502. }
  503. if (InputInformation->Out == InputInformation->In) {
  504. *ResetWaitEvent = TRUE;
  505. }
  506. *EventsRead = 1;
  507. return STATUS_SUCCESS;
  508. }
  509. BufferLengthInBytes = Length * sizeof(INPUT_RECORD);
  510. //
  511. // if in > out, buffer looks like this:
  512. //
  513. // out in
  514. // ______ _____________
  515. // | | | |
  516. // | free | data | free |
  517. // |______|______|______|
  518. //
  519. // we transfer the requested number of events or the amount in the buffer
  520. //
  521. if (InputInformation->In > InputInformation->Out) {
  522. if ((InputInformation->In - InputInformation->Out) > BufferLengthInBytes) {
  523. TransferLength = BufferLengthInBytes;
  524. }
  525. else {
  526. TransferLength = (ULONG)(InputInformation->In - InputInformation->Out);
  527. }
  528. #ifdef FE_SB
  529. if (!Unicode) {
  530. BufferLengthInBytes = 0;
  531. OldTransferLength = TransferLength / sizeof(INPUT_RECORD);
  532. BufferRecords = (PINPUT_RECORD)Buffer;
  533. QueueRecords = (PINPUT_RECORD)InputInformation->Out;
  534. while (BufferLengthInBytes < Length &&
  535. OldTransferLength) {
  536. UniChar = QueueRecords->Event.KeyEvent.uChar.UnicodeChar;
  537. EventType = QueueRecords->EventType;
  538. *BufferRecords++ = *QueueRecords++;
  539. if (EventType == KEY_EVENT) {
  540. if (IsConsoleFullWidth(Console->hDC,
  541. Console->CP,
  542. UniChar)) {
  543. BufferLengthInBytes += 2;
  544. }
  545. else {
  546. BufferLengthInBytes++;
  547. }
  548. }
  549. else {
  550. BufferLengthInBytes++;
  551. }
  552. OldTransferLength--;
  553. }
  554. UserAssert(TransferLength >= OldTransferLength * sizeof(INPUT_RECORD));
  555. TransferLength -= OldTransferLength * sizeof(INPUT_RECORD);
  556. }
  557. else
  558. #endif
  559. {
  560. RtlMoveMemory((PBYTE)Buffer,
  561. (PBYTE)InputInformation->Out,
  562. TransferLength
  563. );
  564. }
  565. *EventsRead = TransferLength / sizeof(INPUT_RECORD);
  566. #ifdef FE_SB
  567. UserAssert(*EventsRead <= Length);
  568. #endif
  569. if (!Peek) {
  570. InputInformation->Out += TransferLength;
  571. #ifdef FE_SB
  572. UserAssert(InputInformation->Out <= InputInformation->Last);
  573. #endif
  574. }
  575. if (InputInformation->Out == InputInformation->In) {
  576. *ResetWaitEvent = TRUE;
  577. }
  578. return STATUS_SUCCESS;
  579. }
  580. //
  581. // if out > in, buffer looks like this:
  582. //
  583. // in out
  584. // ______ _____________
  585. // | | | |
  586. // | data | free | data |
  587. // |______|______|______|
  588. //
  589. // we read from the out pointer to the end of the buffer then from the
  590. // beginning of the buffer, until we hit the in pointer or enough bytes
  591. // are read.
  592. //
  593. else {
  594. if ((InputInformation->Last - InputInformation->Out) > BufferLengthInBytes) {
  595. TransferLength = BufferLengthInBytes;
  596. }
  597. else {
  598. TransferLength = (ULONG)(InputInformation->Last - InputInformation->Out);
  599. }
  600. #ifdef FE_SB
  601. if (!Unicode) {
  602. BufferLengthInBytes = 0;
  603. OldTransferLength = TransferLength / sizeof(INPUT_RECORD);
  604. BufferRecords = (PINPUT_RECORD)Buffer;
  605. QueueRecords = (PINPUT_RECORD)InputInformation->Out;
  606. while (BufferLengthInBytes < Length &&
  607. OldTransferLength) {
  608. UniChar = QueueRecords->Event.KeyEvent.uChar.UnicodeChar;
  609. EventType = QueueRecords->EventType;
  610. *BufferRecords++ = *QueueRecords++;
  611. if (EventType == KEY_EVENT) {
  612. if (IsConsoleFullWidth(Console->hDC,
  613. Console->CP,
  614. UniChar)) {
  615. BufferLengthInBytes += 2;
  616. }
  617. else {
  618. BufferLengthInBytes++;
  619. }
  620. }
  621. else {
  622. BufferLengthInBytes++;
  623. }
  624. OldTransferLength--;
  625. }
  626. UserAssert(TransferLength >= OldTransferLength * sizeof(INPUT_RECORD));
  627. TransferLength -= OldTransferLength * sizeof(INPUT_RECORD);
  628. }
  629. else
  630. #endif
  631. {
  632. RtlMoveMemory((PBYTE)Buffer,
  633. (PBYTE)InputInformation->Out,
  634. TransferLength
  635. );
  636. }
  637. *EventsRead = TransferLength / sizeof(INPUT_RECORD);
  638. #ifdef FE_SB
  639. UserAssert(*EventsRead <= Length);
  640. #endif
  641. if (!Peek) {
  642. InputInformation->Out += TransferLength;
  643. #ifdef FE_SB
  644. UserAssert(InputInformation->Out <= InputInformation->Last);
  645. #endif
  646. if (InputInformation->Out == InputInformation->Last) {
  647. InputInformation->Out = InputInformation->First;
  648. }
  649. }
  650. #ifdef FE_SB
  651. if (!Unicode) {
  652. if (BufferLengthInBytes >= Length) {
  653. if (InputInformation->Out == InputInformation->In) {
  654. *ResetWaitEvent = TRUE;
  655. }
  656. return STATUS_SUCCESS;
  657. }
  658. }
  659. else
  660. #endif
  661. if (*EventsRead == Length) {
  662. if (InputInformation->Out == InputInformation->In) {
  663. *ResetWaitEvent = TRUE;
  664. }
  665. return STATUS_SUCCESS;
  666. }
  667. //
  668. // hit end of buffer, read from beginning
  669. //
  670. OldTransferLength = TransferLength;
  671. #ifdef FE_SB
  672. Length2 = Length;
  673. if (!Unicode) {
  674. UserAssert(Length > BufferLengthInBytes);
  675. Length -= BufferLengthInBytes;
  676. if (Length == 0) {
  677. if (InputInformation->Out == InputInformation->In) {
  678. *ResetWaitEvent = TRUE;
  679. }
  680. return STATUS_SUCCESS;
  681. }
  682. BufferLengthInBytes = Length * sizeof(INPUT_RECORD);
  683. if ((InputInformation->In - InputInformation->First) > BufferLengthInBytes) {
  684. TransferLength = BufferLengthInBytes;
  685. }
  686. else {
  687. TransferLength = (ULONG)(InputInformation->In - InputInformation->First);
  688. }
  689. }
  690. else
  691. #endif
  692. if ((InputInformation->In - InputInformation->First) > (BufferLengthInBytes - OldTransferLength)) {
  693. TransferLength = BufferLengthInBytes - OldTransferLength;
  694. }
  695. else {
  696. TransferLength = (ULONG)(InputInformation->In - InputInformation->First);
  697. }
  698. #ifdef FE_SB
  699. if (!Unicode) {
  700. BufferLengthInBytes = 0;
  701. OldTransferLength = TransferLength / sizeof(INPUT_RECORD);
  702. QueueRecords = (PINPUT_RECORD)InputInformation->First;
  703. while (BufferLengthInBytes < Length &&
  704. OldTransferLength) {
  705. UniChar = QueueRecords->Event.KeyEvent.uChar.UnicodeChar;
  706. EventType = QueueRecords->EventType;
  707. *BufferRecords++ = *QueueRecords++;
  708. if (EventType == KEY_EVENT) {
  709. if (IsConsoleFullWidth(Console->hDC,
  710. Console->CP,
  711. UniChar)) {
  712. BufferLengthInBytes += 2;
  713. }
  714. else {
  715. BufferLengthInBytes++;
  716. }
  717. }
  718. else {
  719. BufferLengthInBytes++;
  720. }
  721. OldTransferLength--;
  722. }
  723. UserAssert(TransferLength >= OldTransferLength * sizeof(INPUT_RECORD));
  724. TransferLength -= OldTransferLength * sizeof(INPUT_RECORD);
  725. }
  726. else
  727. #endif
  728. {
  729. RtlMoveMemory((PBYTE)Buffer+OldTransferLength,
  730. (PBYTE)InputInformation->First,
  731. TransferLength
  732. );
  733. }
  734. *EventsRead += TransferLength / sizeof(INPUT_RECORD);
  735. #ifdef FE_SB
  736. UserAssert(*EventsRead <= Length2);
  737. #endif
  738. if (!Peek) {
  739. InputInformation->Out = InputInformation->First + TransferLength;
  740. }
  741. if (InputInformation->Out == InputInformation->In) {
  742. *ResetWaitEvent = TRUE;
  743. }
  744. return STATUS_SUCCESS;
  745. }
  746. }
  747. NTSTATUS
  748. ReadInputBuffer(
  749. IN PINPUT_INFORMATION InputInformation,
  750. OUT PINPUT_RECORD lpBuffer,
  751. IN OUT PDWORD nLength,
  752. IN BOOL Peek,
  753. IN BOOL WaitForData,
  754. IN BOOL StreamRead,
  755. IN PCONSOLE_INFORMATION Console,
  756. IN PHANDLE_DATA HandleData OPTIONAL,
  757. IN PCSR_API_MSG Message OPTIONAL,
  758. IN CSR_WAIT_ROUTINE WaitRoutine OPTIONAL,
  759. IN PVOID WaitParameter OPTIONAL,
  760. IN ULONG WaitParameterLength OPTIONAL,
  761. IN BOOLEAN WaitBlockExists OPTIONAL
  762. #if defined(FE_SB)
  763. ,
  764. IN BOOLEAN Unicode
  765. #endif
  766. )
  767. /*++
  768. Routine Description:
  769. This routine reads from the input buffer.
  770. Arguments:
  771. InputInformation - Pointer to input buffer information structure.
  772. lpBuffer - Buffer to read into.
  773. nLength - On input, number of events to read. On output, number of
  774. events read.
  775. Peek - If TRUE, copy events to lpBuffer but don't remove them from
  776. the input buffer.
  777. WaitForData - if TRUE, wait until an event is input. if FALSE, return
  778. immediately
  779. StreamRead - if TRUE, events with repeat counts > 1 are returned
  780. as multiple events. also, EventsRead == 1.
  781. Console - Pointer to console buffer information.
  782. HandleData - Pointer to handle data structure. This parameter is
  783. optional if WaitForData is false.
  784. Message - if called from dll (not InputThread), points to api
  785. message. this parameter is used for wait block processing.
  786. WaitRoutine - Routine to call when wait is woken up.
  787. WaitParameter - Parameter to pass to wait routine.
  788. WaitParameterLength - Length of wait parameter.
  789. WaitBlockExists - TRUE if wait block has already been created.
  790. Return Value:
  791. Note:
  792. The console lock must be held when calling this routine.
  793. --*/
  794. {
  795. ULONG EventsRead;
  796. NTSTATUS Status;
  797. BOOL ResetWaitEvent;
  798. if (InputInformation->In == InputInformation->Out) {
  799. if (!WaitForData) {
  800. *nLength = 0;
  801. return STATUS_SUCCESS;
  802. }
  803. LockReadCount(HandleData);
  804. HandleData->InputReadData->ReadCount += 1;
  805. UnlockReadCount(HandleData);
  806. Status = WaitForMoreToRead(InputInformation,
  807. Message,
  808. WaitRoutine,
  809. WaitParameter,
  810. WaitParameterLength,
  811. WaitBlockExists
  812. );
  813. if (!NT_SUCCESS(Status)) {
  814. if (Status != CONSOLE_STATUS_WAIT) {
  815. /*
  816. * WaitForMoreToRead failed, restore ReadCount and bale out
  817. */
  818. LockReadCount(HandleData);
  819. HandleData->InputReadData->ReadCount -= 1;
  820. UnlockReadCount(HandleData);
  821. }
  822. *nLength = 0;
  823. return Status;
  824. }
  825. //
  826. // we will only get to this point if we were called by GetInput.
  827. //
  828. UserAssert(FALSE); // I say we never get here ! IANJA
  829. LockConsole(Console);
  830. }
  831. //
  832. // read from buffer
  833. //
  834. Status = ReadBuffer(InputInformation,
  835. lpBuffer,
  836. *nLength,
  837. &EventsRead,
  838. Peek,
  839. StreamRead,
  840. &ResetWaitEvent
  841. #if defined(FE_SB)
  842. ,
  843. Unicode
  844. #endif
  845. );
  846. if (ResetWaitEvent) {
  847. NtClearEvent(InputInformation->InputWaitEvent);
  848. }
  849. *nLength = EventsRead;
  850. return Status;
  851. }
  852. NTSTATUS
  853. WriteBuffer(
  854. OUT PINPUT_INFORMATION InputInformation,
  855. IN PVOID Buffer,
  856. IN ULONG Length,
  857. OUT PULONG EventsWritten,
  858. OUT PBOOL SetWaitEvent
  859. )
  860. /*++
  861. Routine Description:
  862. This routine writes to a buffer. It does the actual circular buffer
  863. manipulation.
  864. Arguments:
  865. InputInformation - buffer to write to
  866. Buffer - buffer to write from
  867. Length - length of buffer in events
  868. BytesRead - where to store number of bytes written.
  869. SetWaitEvent - on exit, TRUE if buffer became non-empty.
  870. Return Value:
  871. ERROR_BROKEN_PIPE - no more readers.
  872. Note:
  873. The console lock must be held when calling this routine.
  874. --*/
  875. {
  876. NTSTATUS Status;
  877. ULONG TransferLength;
  878. ULONG BufferLengthInBytes;
  879. #if defined(FE_SB)
  880. PCONSOLE_INFORMATION Console = InputInformation->Console;
  881. #endif
  882. *SetWaitEvent = FALSE;
  883. //
  884. // windows sends a mouse_move message each time a window is updated.
  885. // coalesce these.
  886. //
  887. if (Length == 1 && InputInformation->Out != InputInformation->In) {
  888. PINPUT_RECORD InputEvent=Buffer;
  889. if (InputEvent->EventType == MOUSE_EVENT &&
  890. InputEvent->Event.MouseEvent.dwEventFlags == MOUSE_MOVED) {
  891. PINPUT_RECORD LastInputEvent;
  892. if (InputInformation->In == InputInformation->First) {
  893. LastInputEvent = (PINPUT_RECORD) (InputInformation->Last - sizeof(INPUT_RECORD));
  894. }
  895. else {
  896. LastInputEvent = (PINPUT_RECORD) (InputInformation->In - sizeof(INPUT_RECORD));
  897. }
  898. if (LastInputEvent->EventType == MOUSE_EVENT &&
  899. LastInputEvent->Event.MouseEvent.dwEventFlags == MOUSE_MOVED) {
  900. LastInputEvent->Event.MouseEvent.dwMousePosition.X =
  901. InputEvent->Event.MouseEvent.dwMousePosition.X;
  902. LastInputEvent->Event.MouseEvent.dwMousePosition.Y =
  903. InputEvent->Event.MouseEvent.dwMousePosition.Y;
  904. *EventsWritten = 1;
  905. return STATUS_SUCCESS;
  906. }
  907. }
  908. else if (InputEvent->EventType == KEY_EVENT &&
  909. InputEvent->Event.KeyEvent.bKeyDown) {
  910. PINPUT_RECORD LastInputEvent;
  911. if (InputInformation->In == InputInformation->First) {
  912. LastInputEvent = (PINPUT_RECORD) (InputInformation->Last - sizeof(INPUT_RECORD));
  913. }
  914. else {
  915. LastInputEvent = (PINPUT_RECORD) (InputInformation->In - sizeof(INPUT_RECORD));
  916. }
  917. #if defined(FE_SB)
  918. if (IsConsoleFullWidth(Console->hDC,
  919. Console->CP,InputEvent->Event.KeyEvent.uChar.UnicodeChar)) {
  920. ;
  921. }
  922. else
  923. if (InputEvent->Event.KeyEvent.dwControlKeyState & NLS_IME_CONVERSION) {
  924. if (LastInputEvent->EventType == KEY_EVENT &&
  925. LastInputEvent->Event.KeyEvent.bKeyDown &&
  926. (LastInputEvent->Event.KeyEvent.uChar.UnicodeChar ==
  927. InputEvent->Event.KeyEvent.uChar.UnicodeChar) &&
  928. (LastInputEvent->Event.KeyEvent.dwControlKeyState ==
  929. InputEvent->Event.KeyEvent.dwControlKeyState) ) {
  930. LastInputEvent->Event.KeyEvent.wRepeatCount +=
  931. InputEvent->Event.KeyEvent.wRepeatCount;
  932. *EventsWritten = 1;
  933. return STATUS_SUCCESS;
  934. }
  935. }
  936. else
  937. #endif
  938. if (LastInputEvent->EventType == KEY_EVENT &&
  939. LastInputEvent->Event.KeyEvent.bKeyDown &&
  940. (LastInputEvent->Event.KeyEvent.wVirtualScanCode == // scancode same
  941. InputEvent->Event.KeyEvent.wVirtualScanCode) &&
  942. (LastInputEvent->Event.KeyEvent.uChar.UnicodeChar == // character same
  943. InputEvent->Event.KeyEvent.uChar.UnicodeChar) &&
  944. (LastInputEvent->Event.KeyEvent.dwControlKeyState == // ctrl/alt/shift state same
  945. InputEvent->Event.KeyEvent.dwControlKeyState) ) {
  946. LastInputEvent->Event.KeyEvent.wRepeatCount +=
  947. InputEvent->Event.KeyEvent.wRepeatCount;
  948. *EventsWritten = 1;
  949. return STATUS_SUCCESS;
  950. }
  951. }
  952. }
  953. BufferLengthInBytes = Length*sizeof(INPUT_RECORD);
  954. *EventsWritten = 0;
  955. while (*EventsWritten < Length) {
  956. //
  957. //
  958. // if out > in, buffer looks like this:
  959. //
  960. // in out
  961. // ______ _____________
  962. // | | | |
  963. // | data | free | data |
  964. // |______|______|______|
  965. //
  966. // we can write from in to out-1
  967. //
  968. if (InputInformation->Out > InputInformation->In) {
  969. TransferLength = BufferLengthInBytes;
  970. if ((InputInformation->Out - InputInformation->In - sizeof(INPUT_RECORD))
  971. < BufferLengthInBytes) {
  972. Status = SetInputBufferSize(InputInformation,
  973. InputInformation->InputBufferSize+Length+INPUT_BUFFER_SIZE_INCREMENT);
  974. if (!NT_SUCCESS(Status)) {
  975. RIPMSG1(RIP_WARNING,
  976. "Couldn't grow input buffer, Status == 0x%x",
  977. Status);
  978. TransferLength = (ULONG)(InputInformation->Out - InputInformation->In - sizeof(INPUT_RECORD));
  979. if (TransferLength == 0) {
  980. return Status;
  981. }
  982. } else {
  983. goto OutPath; // after resizing, in > out
  984. }
  985. }
  986. RtlMoveMemory((PBYTE)InputInformation->In,
  987. (PBYTE)Buffer,
  988. TransferLength
  989. );
  990. Buffer = (PVOID) (((PBYTE) Buffer)+TransferLength);
  991. *EventsWritten += TransferLength/sizeof(INPUT_RECORD);
  992. BufferLengthInBytes -= TransferLength;
  993. InputInformation->In += TransferLength;
  994. }
  995. //
  996. // if in >= out, buffer looks like this:
  997. //
  998. // out in
  999. // ______ _____________
  1000. // | | | |
  1001. // | free | data | free |
  1002. // |______|______|______|
  1003. //
  1004. // we write from the in pointer to the end of the buffer then from the
  1005. // beginning of the buffer, until we hit the out pointer or enough bytes
  1006. // are written.
  1007. //
  1008. else {
  1009. if (InputInformation->Out == InputInformation->In) {
  1010. *SetWaitEvent = TRUE;
  1011. }
  1012. OutPath:
  1013. if ((InputInformation->Last - InputInformation->In) > BufferLengthInBytes) {
  1014. TransferLength = BufferLengthInBytes;
  1015. }
  1016. else {
  1017. if (InputInformation->First == InputInformation->Out &&
  1018. InputInformation->In == (InputInformation->Last-sizeof(INPUT_RECORD))) {
  1019. TransferLength = BufferLengthInBytes;
  1020. Status = SetInputBufferSize(InputInformation,
  1021. InputInformation->InputBufferSize+Length+INPUT_BUFFER_SIZE_INCREMENT);
  1022. if (!NT_SUCCESS(Status)) {
  1023. RIPMSG1(RIP_WARNING,
  1024. "Couldn't grow input buffer, Status == 0x%x",
  1025. Status);
  1026. return Status;
  1027. }
  1028. }
  1029. else {
  1030. TransferLength = (ULONG)(InputInformation->Last - InputInformation->In);
  1031. if (InputInformation->First == InputInformation->Out) {
  1032. TransferLength -= sizeof(INPUT_RECORD);
  1033. }
  1034. }
  1035. }
  1036. RtlMoveMemory((PBYTE)InputInformation->In,
  1037. (PBYTE)Buffer,
  1038. TransferLength
  1039. );
  1040. Buffer = (PVOID) (((PBYTE) Buffer)+TransferLength);
  1041. *EventsWritten += TransferLength/sizeof(INPUT_RECORD);
  1042. BufferLengthInBytes -= TransferLength;
  1043. InputInformation->In += TransferLength;
  1044. if (InputInformation->In == InputInformation->Last) {
  1045. InputInformation->In = InputInformation->First;
  1046. }
  1047. }
  1048. if (TransferLength == 0) {
  1049. UserAssert(FALSE);
  1050. }
  1051. }
  1052. return STATUS_SUCCESS;
  1053. }
  1054. __inline BOOL
  1055. IsSystemKey(
  1056. WORD wVirtualKeyCode
  1057. )
  1058. {
  1059. switch (wVirtualKeyCode) {
  1060. case VK_SHIFT:
  1061. case VK_CONTROL:
  1062. case VK_MENU:
  1063. case VK_PAUSE:
  1064. case VK_CAPITAL:
  1065. case VK_LWIN:
  1066. case VK_RWIN:
  1067. case VK_NUMLOCK:
  1068. case VK_SCROLL:
  1069. return TRUE;
  1070. }
  1071. return FALSE;
  1072. }
  1073. DWORD
  1074. PreprocessInput(
  1075. IN PCONSOLE_INFORMATION Console,
  1076. IN PINPUT_RECORD InputEvent,
  1077. IN DWORD nLength
  1078. )
  1079. /*++
  1080. Routine Description:
  1081. This routine processes special characters in the input stream.
  1082. Arguments:
  1083. Console - Pointer to console structure.
  1084. InputEvent - Buffer to write from.
  1085. nLength - Number of events to write.
  1086. Return Value:
  1087. Number of events to write after special characters have been stripped.
  1088. Note:
  1089. The console lock must be held when calling this routine.
  1090. --*/
  1091. {
  1092. ULONG NumEvents;
  1093. for (NumEvents = nLength; NumEvents != 0; NumEvents--) {
  1094. if (InputEvent->EventType == KEY_EVENT && InputEvent->Event.KeyEvent.bKeyDown) {
  1095. //
  1096. // if output is suspended, any keyboard input releases it.
  1097. //
  1098. if ((Console->Flags & CONSOLE_SUSPENDED) &&
  1099. !IsSystemKey(InputEvent->Event.KeyEvent.wVirtualKeyCode)) {
  1100. UnblockWriteConsole(Console, CONSOLE_OUTPUT_SUSPENDED);
  1101. RtlMoveMemory(InputEvent, InputEvent + 1, (NumEvents - 1) * sizeof(INPUT_RECORD));
  1102. nLength--;
  1103. continue;
  1104. }
  1105. //
  1106. // intercept control-s
  1107. //
  1108. if ((Console->InputBuffer.InputMode & ENABLE_LINE_INPUT) &&
  1109. (InputEvent->Event.KeyEvent.wVirtualKeyCode == VK_PAUSE ||
  1110. IsPauseKey(&InputEvent->Event.KeyEvent))) {
  1111. Console->Flags |= CONSOLE_OUTPUT_SUSPENDED;
  1112. RtlMoveMemory(InputEvent, InputEvent + 1, (NumEvents - 1) * sizeof(INPUT_RECORD));
  1113. nLength--;
  1114. continue;
  1115. }
  1116. }
  1117. InputEvent++;
  1118. }
  1119. return nLength;
  1120. }
  1121. DWORD
  1122. PrependInputBuffer(
  1123. IN PCONSOLE_INFORMATION Console,
  1124. IN PINPUT_INFORMATION InputInformation,
  1125. IN PINPUT_RECORD lpBuffer,
  1126. IN DWORD nLength
  1127. )
  1128. /*++
  1129. Routine Description:
  1130. This routine writes to the beginning of the input buffer.
  1131. Arguments:
  1132. InputInformation - Pointer to input buffer information structure.
  1133. lpBuffer - Buffer to write from.
  1134. nLength - On input, number of events to write. On output, number of
  1135. events written.
  1136. Return Value:
  1137. Note:
  1138. The console lock must be held when calling this routine.
  1139. --*/
  1140. {
  1141. NTSTATUS Status;
  1142. ULONG EventsWritten,EventsRead;
  1143. BOOL SetWaitEvent;
  1144. ULONG NumExistingEvents;
  1145. PINPUT_RECORD pExistingEvents;
  1146. BOOL Dummy;
  1147. nLength = PreprocessInput(Console, lpBuffer, nLength);
  1148. if (nLength == 0) {
  1149. return 0;
  1150. }
  1151. Status = GetNumberOfReadyEvents(InputInformation,
  1152. &NumExistingEvents
  1153. );
  1154. if (NumExistingEvents) {
  1155. pExistingEvents = ConsoleHeapAlloc(BUFFER_TAG, NumExistingEvents * sizeof(INPUT_RECORD));
  1156. if (pExistingEvents == NULL)
  1157. return (DWORD)STATUS_NO_MEMORY;
  1158. Status = ReadBuffer(InputInformation,
  1159. pExistingEvents,
  1160. NumExistingEvents,
  1161. &EventsRead,
  1162. FALSE,
  1163. FALSE,
  1164. &Dummy
  1165. #if defined(FE_SB)
  1166. ,
  1167. TRUE
  1168. #endif
  1169. );
  1170. if (!NT_SUCCESS(Status)) {
  1171. ConsoleHeapFree(pExistingEvents);
  1172. return Status;
  1173. }
  1174. } else {
  1175. pExistingEvents = NULL;
  1176. }
  1177. //
  1178. // write new info to buffer
  1179. //
  1180. Status = WriteBuffer(InputInformation,
  1181. lpBuffer,
  1182. nLength,
  1183. &EventsWritten,
  1184. &SetWaitEvent
  1185. );
  1186. //
  1187. // write existing info to buffer
  1188. //
  1189. if (pExistingEvents) {
  1190. Status = WriteBuffer(InputInformation,
  1191. pExistingEvents,
  1192. EventsRead,
  1193. &EventsWritten,
  1194. &Dummy
  1195. );
  1196. ConsoleHeapFree(pExistingEvents);
  1197. }
  1198. if (SetWaitEvent) {
  1199. NtSetEvent(InputInformation->InputWaitEvent,NULL);
  1200. }
  1201. //
  1202. // alert any writers waiting for space
  1203. //
  1204. WakeUpReadersWaitingForData(Console,InputInformation);
  1205. return nLength;
  1206. }
  1207. DWORD
  1208. WriteInputBuffer(
  1209. IN PCONSOLE_INFORMATION Console,
  1210. IN PINPUT_INFORMATION InputInformation,
  1211. IN PINPUT_RECORD lpBuffer,
  1212. IN DWORD nLength
  1213. )
  1214. /*++
  1215. Routine Description:
  1216. This routine writes to the input buffer.
  1217. Arguments:
  1218. InputInformation - Pointer to input buffer information structure.
  1219. lpBuffer - Buffer to write from.
  1220. nLength - On input, number of events to write. On output, number of
  1221. events written.
  1222. Return Value:
  1223. Note:
  1224. The console lock must be held when calling this routine.
  1225. --*/
  1226. {
  1227. ULONG EventsWritten;
  1228. BOOL SetWaitEvent;
  1229. nLength = PreprocessInput(Console, lpBuffer, nLength);
  1230. if (nLength == 0) {
  1231. return 0;
  1232. }
  1233. //
  1234. // write to buffer
  1235. //
  1236. WriteBuffer(InputInformation,
  1237. lpBuffer,
  1238. nLength,
  1239. &EventsWritten,
  1240. &SetWaitEvent
  1241. );
  1242. if (SetWaitEvent) {
  1243. NtSetEvent(InputInformation->InputWaitEvent,NULL);
  1244. }
  1245. //
  1246. // alert any writers waiting for space
  1247. //
  1248. WakeUpReadersWaitingForData(Console,InputInformation);
  1249. return EventsWritten;
  1250. }
  1251. VOID
  1252. StoreKeyInfo(
  1253. IN PMSG msg)
  1254. {
  1255. int i;
  1256. for (i=0;i<CONSOLE_MAX_KEY_INFO;i++) {
  1257. if (ConsoleKeyInfo[i].hWnd == CONSOLE_FREE_KEY_INFO ||
  1258. ConsoleKeyInfo[i].hWnd == msg->hwnd) {
  1259. break;
  1260. }
  1261. }
  1262. if (i!=CONSOLE_MAX_KEY_INFO) {
  1263. ConsoleKeyInfo[i].hWnd = msg->hwnd;
  1264. ConsoleKeyInfo[i].wVirtualKeyCode = LOWORD(msg->wParam);
  1265. ConsoleKeyInfo[i].wVirtualScanCode = (BYTE)(HIWORD(msg->lParam));
  1266. } else {
  1267. RIPMSG0(RIP_WARNING, "ConsoleKeyInfo buffer is full");
  1268. }
  1269. }
  1270. VOID
  1271. RetrieveKeyInfo(
  1272. IN HWND hWnd,
  1273. OUT PWORD pwVirtualKeyCode,
  1274. OUT PWORD pwVirtualScanCode,
  1275. IN BOOL FreeKeyInfo)
  1276. {
  1277. int i;
  1278. for (i = 0; i < CONSOLE_MAX_KEY_INFO; i++) {
  1279. if (ConsoleKeyInfo[i].hWnd == hWnd) {
  1280. break;
  1281. }
  1282. }
  1283. if (i != CONSOLE_MAX_KEY_INFO) {
  1284. *pwVirtualKeyCode = ConsoleKeyInfo[i].wVirtualKeyCode;
  1285. *pwVirtualScanCode = ConsoleKeyInfo[i].wVirtualScanCode;
  1286. if (FreeKeyInfo) {
  1287. ConsoleKeyInfo[i].hWnd = CONSOLE_FREE_KEY_INFO;
  1288. }
  1289. } else {
  1290. *pwVirtualKeyCode = (WORD)MapVirtualKey(*pwVirtualScanCode, 3);
  1291. }
  1292. }
  1293. VOID
  1294. ClearKeyInfo(
  1295. IN HWND hWnd
  1296. )
  1297. {
  1298. int i;
  1299. for (i=0;i<CONSOLE_MAX_KEY_INFO;i++) {
  1300. if (ConsoleKeyInfo[i].hWnd == hWnd) {
  1301. ConsoleKeyInfo[i].hWnd = CONSOLE_FREE_KEY_INFO;
  1302. }
  1303. }
  1304. }
  1305. /***************************************************************************\
  1306. * ProcessCreateConsoleWindow
  1307. *
  1308. * This routine processes a CM_CREATE_CONSOLE_WINDOW message. It is called
  1309. * from the InputThread message loop under normal circumstances and from
  1310. * the DialogHookProc if we have a dialog box up. The USER critical section
  1311. * should not be held when calling this routine.
  1312. \***************************************************************************/
  1313. VOID
  1314. ProcessCreateConsoleWindow(
  1315. IN LPMSG lpMsg)
  1316. {
  1317. NTSTATUS Status;
  1318. PCONSOLE_INFORMATION pConsole;
  1319. if (NT_SUCCESS(RevalidateConsole((HANDLE)lpMsg->wParam, &pConsole))) {
  1320. //
  1321. // Make sure the console doesn't already have a window.
  1322. //
  1323. if (pConsole->hWnd) {
  1324. RIPMSG1(RIP_WARNING, "Console %#p already has a window", pConsole);
  1325. UnlockConsole(pConsole);
  1326. return;
  1327. }
  1328. pConsole->InputThreadInfo = TlsGetValue(InputThreadTlsIndex);
  1329. Status = CreateWindowsWindow(pConsole);
  1330. switch (Status) {
  1331. case STATUS_SUCCESS:
  1332. case STATUS_NO_MEMORY:
  1333. UnlockConsole(pConsole);
  1334. break;
  1335. case STATUS_INVALID_HANDLE:
  1336. // Console is gone, don't do anything.
  1337. break;
  1338. default:
  1339. RIPMSG1(RIP_ERROR, "CreateWindowsWindow returned %x", Status);
  1340. break;
  1341. }
  1342. }
  1343. }
  1344. LRESULT
  1345. DialogHookProc(
  1346. int nCode,
  1347. WPARAM wParam,
  1348. LPARAM lParam
  1349. )
  1350. // this routine gets called to filter input to console dialogs so
  1351. // that we can do the special processing that StoreKeyInfo does.
  1352. {
  1353. MSG *pmsg = (PMSG)lParam;
  1354. UNREFERENCED_PARAMETER(wParam);
  1355. if (pmsg->message == CM_CREATE_CONSOLE_WINDOW) {
  1356. ProcessCreateConsoleWindow(pmsg);
  1357. return TRUE;
  1358. }
  1359. if (CONSOLE_IS_IME_ENABLED()) {
  1360. if (pmsg->message == CM_CONSOLE_INPUT_THREAD_MSG) {
  1361. PINPUT_THREAD_INFO pThreadInfo = TlsGetValue(InputThreadTlsIndex);
  1362. MSG msg;
  1363. UserAssert(pThreadInfo);
  1364. if (UnqueueThreadMessage(pThreadInfo->ThreadId, &msg.message, &msg.wParam, &msg.lParam)) {
  1365. RIPMSG3(RIP_WARNING, "DialogHookProc: %04x (%08x, %08x)", msg.message, msg.wParam, msg.lParam);
  1366. switch (msg.message) {
  1367. case CM_CONIME_CREATE:
  1368. ProcessCreateConsoleIME(&msg, pThreadInfo->ThreadId);
  1369. return TRUE;
  1370. case CM_WAIT_CONIME_PROCESS:
  1371. WaitConsoleIMEStuff((HDESK)msg.wParam, (HANDLE)msg.lParam);
  1372. return TRUE;
  1373. case CM_SET_CONSOLEIME_WINDOW:
  1374. pThreadInfo->hWndConsoleIME = (HWND)msg.wParam;
  1375. return TRUE;
  1376. default:
  1377. RIPMSG1(RIP_WARNING, "DialogHookProc: invalid thread message(%04x) !!", msg.message);
  1378. break;
  1379. }
  1380. }
  1381. else {
  1382. RIPMSG0(RIP_WARNING, "DialogHookProc: bogus thread message is posted. ignored");
  1383. }
  1384. }
  1385. }
  1386. if (nCode == MSGF_DIALOGBOX) {
  1387. if (pmsg->message >= WM_KEYFIRST &&
  1388. pmsg->message <= WM_KEYLAST) {
  1389. if (pmsg->message != WM_CHAR &&
  1390. pmsg->message != WM_DEADCHAR &&
  1391. pmsg->message != WM_SYSCHAR &&
  1392. pmsg->message != WM_SYSDEADCHAR) {
  1393. // don't store key info if dialog box input
  1394. if (GetWindowLongPtr(pmsg->hwnd, GWLP_HWNDPARENT) == 0) {
  1395. StoreKeyInfo(pmsg);
  1396. }
  1397. }
  1398. }
  1399. }
  1400. return 0;
  1401. }
  1402. #undef DbgPrint // Need this to build on free systems
  1403. ULONG InputExceptionFilter(
  1404. PEXCEPTION_POINTERS pexi)
  1405. {
  1406. NTSTATUS Status;
  1407. SYSTEM_KERNEL_DEBUGGER_INFORMATION KernelDebuggerInfo;
  1408. if (pexi->ExceptionRecord->ExceptionCode != STATUS_PORT_DISCONNECTED) {
  1409. Status = NtQuerySystemInformation( SystemKernelDebuggerInformation,
  1410. &KernelDebuggerInfo,
  1411. sizeof(KernelDebuggerInfo),
  1412. NULL
  1413. );
  1414. if (NT_SUCCESS(Status) && KernelDebuggerInfo.KernelDebuggerEnabled) {
  1415. DbgPrint("Unhandled Exception hit in csrss.exe InputExceptionFilter\n");
  1416. DbgPrint("first, enter .exr %p for the exception record\n", pexi->ExceptionRecord);
  1417. DbgPrint("next, enter .cxr %p for the context\n", pexi->ContextRecord);
  1418. DbgPrint("then kb to get the faulting stack\n");
  1419. DbgBreakPoint();
  1420. }
  1421. }
  1422. return EXCEPTION_EXECUTE_HANDLER;
  1423. }
  1424. /////////////////////////////////////////
  1425. // Input Thread internal Message Queue:
  1426. // Mainly used for Console IME stuff
  1427. /////////////////////////////////////////
  1428. LIST_ENTRY gInputThreadMsg;
  1429. CRITICAL_SECTION gInputThreadMsgLock;
  1430. VOID
  1431. InitializeThreadMessages()
  1432. {
  1433. RtlEnterCriticalSection(&gInputThreadMsgLock);
  1434. InitializeListHead(&gInputThreadMsg);
  1435. RtlLeaveCriticalSection(&gInputThreadMsgLock);
  1436. }
  1437. VOID
  1438. CleanupInputThreadMessages(
  1439. DWORD dwThreadId)
  1440. {
  1441. UINT message;
  1442. WPARAM wParam;
  1443. LPARAM lParam;
  1444. UserAssert(dwThreadId);
  1445. while (UnqueueThreadMessage(dwThreadId, &message, &wParam, &lParam)) {
  1446. RIPMSG3(RIP_WARNING, "CleanupInputThreadMessages: %04x (%08x, %08x)", message, wParam, lParam);
  1447. }
  1448. }
  1449. //
  1450. // QueueThreadMessage
  1451. //
  1452. // Posts a message to Input Thread, specified by dwThreadId.
  1453. // CM_CONSOLE_INPUT_THEAD_MSG is used as a stub message. Actual parameters are
  1454. // stored in gInputThreadMsg. Input thread should call UnqueueThreadMessage
  1455. // when it gets CM_CONSOLE_INPUT_THREAD_MSG.
  1456. //
  1457. NTSTATUS
  1458. QueueThreadMessage(
  1459. DWORD dwThreadId,
  1460. UINT message,
  1461. WPARAM wParam,
  1462. LPARAM lParam)
  1463. {
  1464. PCONSOLE_THREAD_MSG pConMsg;
  1465. RIPMSG4(RIP_VERBOSE, "QueueThreadMessage: TID=%08x msg:%04x (%08x, %08x)",
  1466. dwThreadId, message, wParam, lParam);
  1467. pConMsg = ConsoleHeapAlloc(TMP_TAG, sizeof *pConMsg);
  1468. if (pConMsg == NULL) {
  1469. RIPMSG0(RIP_WARNING, "QueueThreadMessage: failed to allocate pConMsg");
  1470. return STATUS_NO_MEMORY;
  1471. }
  1472. pConMsg->dwThreadId = dwThreadId;
  1473. pConMsg->Message = message;
  1474. pConMsg->wParam = wParam;
  1475. pConMsg->lParam = lParam;
  1476. RtlEnterCriticalSection(&gInputThreadMsgLock);
  1477. InsertHeadList(&gInputThreadMsg, &pConMsg->ListLink);
  1478. RtlLeaveCriticalSection(&gInputThreadMsgLock);
  1479. if (!PostThreadMessage(dwThreadId, CM_CONSOLE_INPUT_THREAD_MSG, 0, 0)) {
  1480. RIPMSG1(RIP_WARNING, "QueueThreadMessage: failed to post thread msg(%04x)", message);
  1481. RtlEnterCriticalSection(&gInputThreadMsgLock);
  1482. RemoveEntryList(&pConMsg->ListLink);
  1483. RtlLeaveCriticalSection(&gInputThreadMsgLock);
  1484. ConsoleHeapFree(pConMsg);
  1485. return STATUS_UNSUCCESSFUL;
  1486. }
  1487. return STATUS_SUCCESS;
  1488. }
  1489. //
  1490. // UnqueueThreadMessage
  1491. //
  1492. // return value:
  1493. // TRUE -- a message found.
  1494. // FALSE -- no message for dwThreadId found.
  1495. //
  1496. BOOL UnqueueThreadMessage(
  1497. DWORD dwThreadId,
  1498. UINT* pMessage,
  1499. WPARAM* pwParam,
  1500. LPARAM* plParam)
  1501. {
  1502. BOOL fResult = FALSE; // if message is found, set this to TRUE
  1503. PLIST_ENTRY pEntry;
  1504. UserAssert(dwThreadId);
  1505. RtlEnterCriticalSection(&gInputThreadMsgLock);
  1506. //
  1507. // Search for dwThreadId message from the tail of the queue.
  1508. //
  1509. pEntry = gInputThreadMsg.Blink;
  1510. while (pEntry != &gInputThreadMsg) {
  1511. PCONSOLE_THREAD_MSG pConMsg = CONTAINING_RECORD(pEntry, CONSOLE_THREAD_MSG, ListLink);
  1512. if (pConMsg->dwThreadId == dwThreadId) {
  1513. *pMessage = pConMsg->Message;
  1514. *pwParam = pConMsg->wParam;
  1515. *plParam = pConMsg->lParam;
  1516. RemoveEntryList(pEntry);
  1517. ConsoleHeapFree(pConMsg);
  1518. fResult = TRUE;
  1519. break;
  1520. }
  1521. pEntry = pEntry->Blink;
  1522. }
  1523. RtlLeaveCriticalSection(&gInputThreadMsgLock);
  1524. return fResult;
  1525. }
  1526. NTSTATUS
  1527. ConsoleInputThread(
  1528. PINPUT_THREAD_INIT_INFO pInputThreadInitInfo)
  1529. {
  1530. MSG msg;
  1531. PTEB Teb = NtCurrentTeb();
  1532. PCSR_THREAD pcsrt = NULL;
  1533. INPUT_THREAD_INFO ThreadInfo;
  1534. int i;
  1535. HANDLE hThread = NULL;
  1536. HHOOK hhook = NULL;
  1537. BOOL fQuit = FALSE;
  1538. CONSOLEDESKTOPCONSOLETHREAD ConsoleDesktopInfo;
  1539. NTSTATUS Status;
  1540. /*
  1541. * Set this thread's desktop to the one we just created/opened.
  1542. * When the very first app is loaded, the desktop hasn't been
  1543. * created yet so the above call might fail. Make sure we don't
  1544. * accidentally call SetThreadDesktop with a NULL pdesk. The
  1545. * first app will create the desktop and open it for itself.
  1546. */
  1547. ThreadInfo.Desktop = pInputThreadInitInfo->DesktopHandle;
  1548. ThreadInfo.WindowCount = 0;
  1549. ThreadInfo.ThreadHandle = pInputThreadInitInfo->ThreadHandle;
  1550. ThreadInfo.ThreadId = HandleToUlong(Teb->ClientId.UniqueThread);
  1551. #if defined(FE_IME)
  1552. ThreadInfo.hWndConsoleIME = NULL;
  1553. #endif
  1554. TlsSetValue(InputThreadTlsIndex, &ThreadInfo);
  1555. ConsoleDesktopInfo.hdesk = pInputThreadInitInfo->DesktopHandle;
  1556. ConsoleDesktopInfo.dwThreadId = HandleToUlong(Teb->ClientId.UniqueThread);
  1557. Status = NtUserConsoleControl(ConsoleDesktopConsoleThread, &ConsoleDesktopInfo, sizeof(ConsoleDesktopInfo));
  1558. if (NT_SUCCESS(Status)) {
  1559. //
  1560. // This call forces the client-side desktop information
  1561. // to be updated.
  1562. //
  1563. pcsrt = CsrConnectToUser();
  1564. if (pcsrt == NULL ||
  1565. !SetThreadDesktop(pInputThreadInitInfo->DesktopHandle)) {
  1566. Status = STATUS_UNSUCCESSFUL;
  1567. } else {
  1568. //
  1569. // Save our thread handle for cleanup purposes
  1570. //
  1571. hThread = pcsrt->ThreadHandle;
  1572. if (!fOneTimeInitialized) {
  1573. InitializeCustomCP();
  1574. //
  1575. // Initialize default screen dimensions. we have to initialize
  1576. // the font info here (in the input thread) so that GDI doesn't
  1577. // get completely confused on process termination (since a
  1578. // process that looks like it's terminating created all the
  1579. // server HFONTS).
  1580. //
  1581. EnumerateFonts(EF_DEFFACE);
  1582. InitializeScreenInfo();
  1583. if (!InitWindowClass()) {
  1584. /*
  1585. * If the class already exists, this means that some other console attempted
  1586. * to initialize and registered the class but failed right after.
  1587. */
  1588. if (GetLastError() == ERROR_CLASS_ALREADY_EXISTS) {
  1589. RIPMSG0(RIP_WARNING, "ConsoleInputThread: Class already exists.");
  1590. } else {
  1591. RIPMSG0(RIP_WARNING, "ConsoleInputThread: InitWindowClass failed.");
  1592. Status = STATUS_UNSUCCESSFUL;
  1593. }
  1594. }
  1595. for (i = 0; i < CONSOLE_MAX_KEY_INFO; i++) {
  1596. ConsoleKeyInfo[i].hWnd = CONSOLE_FREE_KEY_INFO;
  1597. }
  1598. ProgmanHandleMessage = RegisterWindowMessage(TEXT(CONSOLE_PROGMAN_HANDLE_MESSAGE));
  1599. }
  1600. }
  1601. hhook = SetWindowsHookEx(WH_MSGFILTER,
  1602. DialogHookProc,
  1603. NULL,
  1604. HandleToUlong(Teb->ClientId.UniqueThread));
  1605. if (hhook == NULL) {
  1606. DWORD dwError = GetLastError();
  1607. /*
  1608. * Unfortunately, there's no way to map a Win32 error code to an
  1609. * NTSTATUS, so let's try to be smart about the most likely reasons
  1610. * this API would fail.
  1611. */
  1612. RIPMSGF1(RIP_WARNING,
  1613. "SetWindowsHookEx failed, GLE: 0x%x.",
  1614. dwError);
  1615. if (dwError == ERROR_NOT_ENOUGH_MEMORY || dwError == ERROR_OUTOFMEMORY) {
  1616. Status = STATUS_NO_MEMORY;
  1617. } else if (dwError == ERROR_ACCESS_DENIED) {
  1618. Status = STATUS_ACCESS_DENIED;
  1619. } else {
  1620. Status = STATUS_UNSUCCESSFUL;
  1621. }
  1622. }
  1623. }
  1624. //
  1625. // If we successfully initialized, the input thread is ready to run.
  1626. // Otherwise, kill the thread.
  1627. //
  1628. pInputThreadInitInfo->InitStatus = Status;
  1629. NtSetEvent(pInputThreadInitInfo->InitCompleteEventHandle, NULL);
  1630. if (!NT_SUCCESS(Status)) {
  1631. RIPMSGF1(RIP_WARNING,
  1632. "Failed to initialize, status 0x%x. Bailing out.",
  1633. Status);
  1634. goto Cleanup;
  1635. }
  1636. while (TRUE) {
  1637. //
  1638. // If a WM_QUIT has been received and all windows
  1639. // are gone, get out.
  1640. //
  1641. if (fQuit && ThreadInfo.WindowCount == 0) {
  1642. break;
  1643. }
  1644. //
  1645. // Make sure we don't hold any locks while we're idle.
  1646. //
  1647. UserAssert(NtCurrentTeb()->CountOfOwnedCriticalSections == 0);
  1648. GetMessage(&msg, NULL, 0, 0);
  1649. //
  1650. // Trap messages posted to the thread.
  1651. //
  1652. if (msg.message == CM_CREATE_CONSOLE_WINDOW) {
  1653. ProcessCreateConsoleWindow(&msg);
  1654. continue;
  1655. } else if (msg.message == WM_QUIT) {
  1656. //
  1657. // The message was posted from ExitWindows. This
  1658. // means that it's OK to terminate the thread.
  1659. //
  1660. fQuit = TRUE;
  1661. //
  1662. // Only exit the loop if there are no windows,
  1663. //
  1664. if (ThreadInfo.WindowCount == 0) {
  1665. break;
  1666. }
  1667. RIPMSG0(RIP_WARNING, "WM_QUIT received by console with windows");
  1668. continue;
  1669. } else if (CONSOLE_IS_IME_ENABLED()) {
  1670. if (msg.message == CM_CONSOLE_INPUT_THREAD_MSG) {
  1671. if (UnqueueThreadMessage(ThreadInfo.ThreadId, &msg.message, &msg.wParam, &msg.lParam)) {
  1672. RIPMSG3(RIP_VERBOSE, "InputThread: Unqueue: msg=%04x (%08x, %08x)", msg.message, msg.wParam, msg.lParam);
  1673. switch (msg.message) {
  1674. case CM_CONIME_CREATE:
  1675. ProcessCreateConsoleIME(&msg, ThreadInfo.ThreadId);
  1676. continue;
  1677. case CM_WAIT_CONIME_PROCESS:
  1678. WaitConsoleIMEStuff((HDESK)msg.wParam, (HANDLE)msg.lParam);
  1679. continue;
  1680. case CM_SET_CONSOLEIME_WINDOW:
  1681. ThreadInfo.hWndConsoleIME = (HWND)msg.wParam;
  1682. continue;
  1683. default:
  1684. RIPMSG1(RIP_WARNING, "ConsoleInputThread: invalid thread message(%04x) !!", msg.message);
  1685. break;
  1686. }
  1687. } else {
  1688. RIPMSGF1(RIP_WARNING,
  1689. "Bogus thread message posted (msg = 0x%x).",
  1690. msg.message);
  1691. continue;
  1692. }
  1693. }
  1694. }
  1695. if (!TranslateMessageEx(&msg, TM_POSTCHARBREAKS)) {
  1696. DispatchMessage(&msg);
  1697. } else {
  1698. // do this so that alt-tab works while journalling
  1699. if (msg.message == WM_SYSKEYDOWN && msg.wParam == VK_TAB &&
  1700. (msg.lParam & 0x20000000) ) { // alt is really down
  1701. DispatchMessage(&msg);
  1702. } else {
  1703. StoreKeyInfo(&msg);
  1704. }
  1705. }
  1706. }
  1707. //
  1708. // Cleanup the input thread messages.
  1709. //
  1710. CleanupInputThreadMessages(ThreadInfo.ThreadId);
  1711. UserAssert(Status == STATUS_SUCCESS);
  1712. Cleanup:
  1713. //
  1714. // Free all resources used by this thread
  1715. //
  1716. if (hhook != NULL) {
  1717. UnhookWindowsHookEx(hhook);
  1718. }
  1719. ConsoleDesktopInfo.dwThreadId = 0;
  1720. NtUserConsoleControl(ConsoleDesktopConsoleThread,
  1721. &ConsoleDesktopInfo,
  1722. sizeof(ConsoleDesktopInfo));
  1723. //
  1724. // Close the desktop handle. CSR is special cased to close
  1725. // the handle even if the thread has windows. The desktop
  1726. // remains assigned to the thread.
  1727. //
  1728. UserVerify(CloseDesktop(ThreadInfo.Desktop));
  1729. //
  1730. // Restore thread handle so that CSR won't get confused.
  1731. //
  1732. if (hThread != NULL) {
  1733. pcsrt->ThreadHandle = hThread;
  1734. }
  1735. if (pcsrt != NULL) {
  1736. CsrDereferenceThread(pcsrt);
  1737. }
  1738. UserExitWorkerThread(Status);
  1739. return Status;
  1740. }
  1741. ULONG
  1742. GetControlKeyState(
  1743. LPARAM lParam
  1744. )
  1745. {
  1746. ULONG ControlKeyState = 0;
  1747. if (GetKeyState(VK_LMENU) & KEY_PRESSED) {
  1748. ControlKeyState |= LEFT_ALT_PRESSED;
  1749. }
  1750. if (GetKeyState(VK_RMENU) & KEY_PRESSED) {
  1751. ControlKeyState |= RIGHT_ALT_PRESSED;
  1752. }
  1753. if (GetKeyState(VK_LCONTROL) & KEY_PRESSED) {
  1754. ControlKeyState |= LEFT_CTRL_PRESSED;
  1755. }
  1756. if (GetKeyState(VK_RCONTROL) & KEY_PRESSED) {
  1757. ControlKeyState |= RIGHT_CTRL_PRESSED;
  1758. }
  1759. if (GetKeyState(VK_SHIFT) & KEY_PRESSED) {
  1760. ControlKeyState |= SHIFT_PRESSED;
  1761. }
  1762. if (GetKeyState(VK_NUMLOCK) & KEY_TOGGLED) {
  1763. ControlKeyState |= NUMLOCK_ON;
  1764. }
  1765. if (GetKeyState(VK_OEM_SCROLL) & KEY_TOGGLED) {
  1766. ControlKeyState |= SCROLLLOCK_ON;
  1767. }
  1768. if (GetKeyState(VK_CAPITAL) & KEY_TOGGLED) {
  1769. ControlKeyState |= CAPSLOCK_ON;
  1770. }
  1771. if (lParam & KEY_ENHANCED) {
  1772. ControlKeyState |= ENHANCED_KEY;
  1773. }
  1774. ControlKeyState |= (lParam & ALTNUMPAD_BIT);
  1775. return ControlKeyState;
  1776. }
  1777. ULONG
  1778. ConvertMouseButtonState(
  1779. IN ULONG Flag,
  1780. IN ULONG State
  1781. )
  1782. {
  1783. if (State & MK_LBUTTON) {
  1784. Flag |= FROM_LEFT_1ST_BUTTON_PRESSED;
  1785. }
  1786. if (State & MK_MBUTTON) {
  1787. Flag |= FROM_LEFT_2ND_BUTTON_PRESSED;
  1788. }
  1789. if (State & MK_RBUTTON) {
  1790. Flag |= RIGHTMOST_BUTTON_PRESSED;
  1791. }
  1792. return Flag;
  1793. }
  1794. VOID
  1795. TerminateRead(
  1796. IN PCONSOLE_INFORMATION Console,
  1797. IN PINPUT_INFORMATION InputInfo,
  1798. IN DWORD Flag
  1799. )
  1800. /*++
  1801. Routine Description:
  1802. This routine wakes up any readers waiting for data when a ctrl-c
  1803. or ctrl-break is input.
  1804. Arguments:
  1805. InputInfo - pointer to input buffer
  1806. Flag - flag indicating whether ctrl-break or ctrl-c was input
  1807. --*/
  1808. {
  1809. BOOLEAN WaitSatisfied;
  1810. WaitSatisfied = CsrNotifyWait(&InputInfo->ReadWaitQueue,
  1811. TRUE,
  1812. NULL,
  1813. IntToPtr(Flag)
  1814. );
  1815. if (WaitSatisfied) {
  1816. // #334370 under stress, WaitQueue may already hold the satisfied waits
  1817. UserAssert((Console->WaitQueue == NULL) ||
  1818. (Console->WaitQueue == &InputInfo->ReadWaitQueue));
  1819. Console->WaitQueue = &InputInfo->ReadWaitQueue;
  1820. }
  1821. }
  1822. BOOL
  1823. HandleSysKeyEvent(
  1824. IN PCONSOLE_INFORMATION Console,
  1825. IN HWND hWnd,
  1826. IN UINT Message,
  1827. IN WPARAM wParam,
  1828. IN LPARAM lParam,
  1829. IN PBOOL pbUnlockConsole
  1830. )
  1831. /*
  1832. returns TRUE if DefWindowProc should be called.
  1833. */
  1834. {
  1835. WORD VirtualKeyCode;
  1836. BOOL bCtrlDown;
  1837. #ifndef i386
  1838. UNREFERENCED_PARAMETER(pbUnlockConsole);
  1839. #endif
  1840. #if defined (FE_IME)
  1841. // Sep.16.1995 Support Console IME by v-HirShi(Hirotoshi Shimizu)
  1842. if (Message == WM_SYSCHAR || Message == WM_SYSDEADCHAR ||
  1843. Message == WM_SYSCHAR+CONIME_KEYDATA || Message == WM_SYSDEADCHAR+CONIME_KEYDATA)
  1844. #else
  1845. if (Message == WM_SYSCHAR || Message == WM_SYSDEADCHAR)
  1846. #endif
  1847. {
  1848. VirtualKeyCode = (WORD)MapVirtualKey(LOBYTE(HIWORD(lParam)), 1);
  1849. } else {
  1850. VirtualKeyCode = LOWORD(wParam);
  1851. }
  1852. //
  1853. // check for ctrl-esc
  1854. //
  1855. bCtrlDown = GetKeyState(VK_CONTROL) & KEY_PRESSED;
  1856. if (VirtualKeyCode == VK_ESCAPE &&
  1857. bCtrlDown &&
  1858. !(GetKeyState(VK_MENU) & KEY_PRESSED) &&
  1859. !(GetKeyState(VK_SHIFT) & KEY_PRESSED) &&
  1860. !(Console->ReserveKeys & CONSOLE_CTRLESC) ) {
  1861. return TRUE; // call DefWindowProc
  1862. }
  1863. if ((lParam & 0x20000000) == 0) { // we're iconic
  1864. //
  1865. // Check for ENTER while iconic (restore accelerator).
  1866. //
  1867. if (VirtualKeyCode == VK_RETURN && !(Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE)) {
  1868. return TRUE; // call DefWindowProc
  1869. } else {
  1870. HandleKeyEvent(Console,
  1871. hWnd,
  1872. Message,
  1873. wParam,
  1874. lParam
  1875. );
  1876. return FALSE;
  1877. }
  1878. }
  1879. if (VirtualKeyCode == VK_RETURN && !bCtrlDown &&
  1880. !(Console->ReserveKeys & CONSOLE_ALTENTER)) {
  1881. #ifdef i386
  1882. if (!(Message & KEY_UP_TRANSITION)) {
  1883. if (FullScreenInitialized && !GetSystemMetrics(SM_REMOTESESSION)) {
  1884. if (Console->FullScreenFlags == 0) {
  1885. ConvertToFullScreen(Console);
  1886. Console->FullScreenFlags = CONSOLE_FULLSCREEN;
  1887. ChangeDispSettings(Console, Console->hWnd, CDS_FULLSCREEN);
  1888. } else {
  1889. ConvertToWindowed(Console);
  1890. Console->FullScreenFlags &= ~CONSOLE_FULLSCREEN;
  1891. ChangeDispSettings(Console, Console->hWnd,0);
  1892. ShowWindow(Console->hWnd, SW_RESTORE);
  1893. }
  1894. } else {
  1895. WCHAR ItemString[70];
  1896. HWND hwnd = Console->hWnd;
  1897. LPWSTR lpTitle;
  1898. /*
  1899. * We must unlock the console around the MessageBox call,
  1900. * since this can block indefinitely long (otherwise, any
  1901. * thread that tries to access this console will get hung
  1902. * trying to acquire its lock).
  1903. */
  1904. lpTitle = ConsoleHeapAlloc(TMP_TAG,
  1905. Console->TitleLength + sizeof(WCHAR));
  1906. if (lpTitle) {
  1907. RtlCopyMemory(lpTitle, Console->Title, Console->TitleLength);
  1908. lpTitle[Console->TitleLength / sizeof(WCHAR)] = 0;
  1909. UnlockConsole(Console);
  1910. *pbUnlockConsole = FALSE;
  1911. LoadString(ghInstance,
  1912. msgNoFullScreen,
  1913. ItemString,
  1914. ARRAY_SIZE(ItemString));
  1915. ++DialogBoxCount;
  1916. MessageBox(hWnd,
  1917. ItemString,
  1918. lpTitle,
  1919. MB_SYSTEMMODAL | MB_OK);
  1920. --DialogBoxCount;
  1921. ConsoleHeapFree(lpTitle);
  1922. }
  1923. }
  1924. }
  1925. #endif
  1926. return FALSE;
  1927. }
  1928. //
  1929. // make sure alt-space gets translated so that the system
  1930. // menu is displayed.
  1931. //
  1932. if (!(GetKeyState(VK_CONTROL) & KEY_PRESSED)) {
  1933. if (VirtualKeyCode == VK_SPACE && !(Console->ReserveKeys & CONSOLE_ALTSPACE)) {
  1934. return TRUE; // call DefWindowProc
  1935. }
  1936. if (VirtualKeyCode == VK_ESCAPE && !(Console->ReserveKeys & CONSOLE_ALTESC)) {
  1937. return TRUE; // call DefWindowProc
  1938. }
  1939. if (VirtualKeyCode == VK_TAB && !(Console->ReserveKeys & CONSOLE_ALTTAB)) {
  1940. return TRUE; // call DefWindowProc
  1941. }
  1942. }
  1943. HandleKeyEvent(Console,
  1944. hWnd,
  1945. Message,
  1946. wParam,
  1947. lParam);
  1948. return FALSE;
  1949. }
  1950. VOID
  1951. HandleKeyEvent(
  1952. IN PCONSOLE_INFORMATION Console,
  1953. IN HWND hWnd,
  1954. IN UINT Message,
  1955. IN WPARAM wParam,
  1956. IN LPARAM lParam
  1957. )
  1958. {
  1959. INPUT_RECORD InputEvent;
  1960. BOOLEAN ContinueProcessing;
  1961. ULONG EventsWritten;
  1962. WORD VirtualKeyCode;
  1963. ULONG ControlKeyState;
  1964. BOOL bKeyDown;
  1965. BOOL bGenerateBreak = FALSE;
  1966. #ifdef FE_SB
  1967. BOOL KeyMessageFromConsoleIME;
  1968. #endif
  1969. #ifdef FE_SB
  1970. // v-HirShi Sep.21.1995 For Console IME
  1971. if ((WM_KEYFIRST+CONIME_KEYDATA) <= Message && Message <= (WM_KEYLAST+CONIME_KEYDATA)) {
  1972. Message -= CONIME_KEYDATA ;
  1973. KeyMessageFromConsoleIME = TRUE;
  1974. } else {
  1975. KeyMessageFromConsoleIME = FALSE;
  1976. }
  1977. #endif
  1978. /*
  1979. * BOGUS for WM_CHAR/WM_DEADCHAR, in which LOWORD(lParam) is a character
  1980. */
  1981. VirtualKeyCode = LOWORD(wParam);
  1982. ControlKeyState = GetControlKeyState(lParam);
  1983. bKeyDown = !(lParam & KEY_TRANSITION_UP);
  1984. //
  1985. // Make sure we retrieve the key info first, or we could chew up
  1986. // unneeded space in the key info table if we bail out early.
  1987. //
  1988. InputEvent.Event.KeyEvent.wVirtualKeyCode = VirtualKeyCode;
  1989. InputEvent.Event.KeyEvent.wVirtualScanCode = (BYTE)(HIWORD(lParam));
  1990. if (Message == WM_CHAR || Message == WM_SYSCHAR ||
  1991. Message == WM_DEADCHAR || Message == WM_SYSDEADCHAR) {
  1992. RetrieveKeyInfo(hWnd,
  1993. &InputEvent.Event.KeyEvent.wVirtualKeyCode,
  1994. &InputEvent.Event.KeyEvent.wVirtualScanCode,
  1995. !(Console->InputBuffer.ImeMode.Open ^ KeyMessageFromConsoleIME));
  1996. VirtualKeyCode = InputEvent.Event.KeyEvent.wVirtualKeyCode;
  1997. }
  1998. //
  1999. // If this is a key up message, should we ignore it? We do this
  2000. // so that if a process reads a line from the input buffer, the
  2001. // key up event won't get put in the buffer after the read
  2002. // completes.
  2003. //
  2004. if (Console->Flags & CONSOLE_IGNORE_NEXT_KEYUP) {
  2005. Console->Flags &= ~CONSOLE_IGNORE_NEXT_KEYUP;
  2006. if (!bKeyDown) {
  2007. return;
  2008. }
  2009. }
  2010. #ifdef FE_SB
  2011. // v-HirShi Sep.21.1995 For Console IME
  2012. if (KeyMessageFromConsoleIME) {
  2013. goto FromConsoleIME ;
  2014. }
  2015. #endif
  2016. if (Console->Flags & CONSOLE_SELECTING) {
  2017. if (!bKeyDown) {
  2018. return;
  2019. }
  2020. //
  2021. // if escape or ctrl-c, cancel selection
  2022. //
  2023. if (!(Console->SelectionFlags & CONSOLE_MOUSE_DOWN) ) {
  2024. if (VirtualKeyCode == VK_ESCAPE ||
  2025. (VirtualKeyCode == 'C' &&
  2026. (GetKeyState(VK_CONTROL) & KEY_PRESSED) )) {
  2027. ClearSelection(Console);
  2028. return;
  2029. } else if (VirtualKeyCode == VK_RETURN) {
  2030. // if return, copy selection
  2031. DoCopy(Console);
  2032. return;
  2033. } else if (gfEnableColorSelection &&
  2034. ('0' <= VirtualKeyCode) && ('9' >= VirtualKeyCode) &&
  2035. (Console->CurrentScreenBuffer->Flags & CONSOLE_TEXTMODE_BUFFER)) {
  2036. BOOLEAN AltPressed, ShiftPressed, CtrlPressed = FALSE;
  2037. PSMALL_RECT Selection = &Console->SelectionRect;
  2038. //
  2039. // It's a numeric key, a text mode buffer and the color selection regkey is set,
  2040. // then check to see if the user want's to color the selection or search and
  2041. // highlight the selection.
  2042. //
  2043. AltPressed = (ControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0;
  2044. ShiftPressed = (ControlKeyState & SHIFT_PRESSED) != 0;
  2045. //
  2046. // Shift implies a find-and-color operation. We only support finding a string, not
  2047. // a block. So if the selected area is > 1 line in height, just ignore the shift
  2048. // and color the selection. Also ignore if there is no current selection.
  2049. //
  2050. if ((ShiftPressed) &&
  2051. ( !(Console->SelectionFlags & CONSOLE_SELECTION_NOT_EMPTY) ||
  2052. (Selection->Top != Selection->Bottom))) {
  2053. ShiftPressed = FALSE;
  2054. }
  2055. //
  2056. // If CTRL + ALT together, then we interpret as ALT (eg on French
  2057. // keyboards AltGr == RALT+LCTRL, but we want it to behave as ALT).
  2058. //
  2059. if (!AltPressed) {
  2060. CtrlPressed = (ControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) != 0;
  2061. }
  2062. //
  2063. // Clip the selection to within the console buffer
  2064. //
  2065. if (Selection->Left < 0) {
  2066. Selection->Left = 0;
  2067. }
  2068. if (Selection->Top < 0) {
  2069. Selection->Top = 0;
  2070. }
  2071. if (Selection->Right >= Console->CurrentScreenBuffer->ScreenBufferSize.X) {
  2072. Selection->Right = (SHORT)(Console->CurrentScreenBuffer->ScreenBufferSize.X-1);
  2073. }
  2074. if (Selection->Bottom >= Console->CurrentScreenBuffer->ScreenBufferSize.Y) {
  2075. Selection->Bottom = (SHORT)(Console->CurrentScreenBuffer->ScreenBufferSize.Y-1);
  2076. }
  2077. //
  2078. // If ALT or CTRL are pressed, then color the selected area.
  2079. // ALT+n => fg, CTRL+n => bg
  2080. //
  2081. if (AltPressed || CtrlPressed) {
  2082. ULONG Attr = VirtualKeyCode - '0' + 6;
  2083. if (CtrlPressed) {
  2084. //
  2085. // Setting background color. Set fg color to black.
  2086. //
  2087. Attr <<= 4;
  2088. } else {
  2089. //
  2090. // Set foreground color. Maintain the current console bg color
  2091. //
  2092. Attr |= Console->CurrentScreenBuffer->Attributes & 0xf0;
  2093. }
  2094. //
  2095. // If shift was pressed as well, then this is actually a find-and-color
  2096. // request. Otherwise just color the selection.
  2097. //
  2098. if (ShiftPressed) {
  2099. WCHAR SearchString[SEARCH_STRING_LENGTH + 1];
  2100. ULONG Length, RowIndex;
  2101. PROW Row;
  2102. PSCREEN_INFORMATION ScreenInfo = Console->CurrentScreenBuffer;
  2103. Length = Selection->Right - Selection->Left + 1;
  2104. if (Length > SEARCH_STRING_LENGTH) {
  2105. Length = SEARCH_STRING_LENGTH;
  2106. }
  2107. //
  2108. // Pull the selection out of the buffer to pass to the search function.
  2109. // Clamp to max search string length. We just copy the bytes out of
  2110. // the row buffer.
  2111. //
  2112. RowIndex = (Selection->Top + ScreenInfo->BufferInfo.TextInfo.FirstRow)
  2113. % ScreenInfo->ScreenBufferSize.Y;
  2114. Row = &ScreenInfo->BufferInfo.TextInfo.Rows[ RowIndex];
  2115. RtlCopyMemory( SearchString,
  2116. &Row->CharRow.Chars[ Selection->Left],
  2117. Length * sizeof( WCHAR));
  2118. SearchString[ Length] = L'\0';
  2119. //
  2120. // Clear the selection, and call the search / mark function.
  2121. //
  2122. ClearSelection(Console);
  2123. SearchForString( ScreenInfo,
  2124. SearchString,
  2125. (USHORT)Length,
  2126. TRUE,
  2127. FALSE,
  2128. TRUE,
  2129. Attr,
  2130. NULL);
  2131. } else {
  2132. ColorSelection( Console, Selection, Attr);
  2133. ClearSelection(Console);
  2134. }
  2135. return;
  2136. }
  2137. }
  2138. }
  2139. if (!(Console->SelectionFlags & CONSOLE_MOUSE_SELECTION)) {
  2140. if ((Console->CurrentScreenBuffer->Flags & CONSOLE_TEXTMODE_BUFFER) &&
  2141. (VirtualKeyCode == VK_RIGHT ||
  2142. VirtualKeyCode == VK_LEFT ||
  2143. VirtualKeyCode == VK_UP ||
  2144. VirtualKeyCode == VK_DOWN ||
  2145. VirtualKeyCode == VK_NEXT ||
  2146. VirtualKeyCode == VK_PRIOR ||
  2147. VirtualKeyCode == VK_END ||
  2148. VirtualKeyCode == VK_HOME
  2149. ) ) {
  2150. PSCREEN_INFORMATION ScreenInfo;
  2151. #ifdef FE_SB
  2152. SHORT RowIndex;
  2153. PROW Row;
  2154. BYTE KAttrs;
  2155. SHORT NextRightX;
  2156. SHORT NextLeftX;
  2157. #endif
  2158. ScreenInfo = Console->CurrentScreenBuffer;
  2159. //
  2160. // see if shift is down. if so, we're extending
  2161. // the selection. otherwise, we're resetting the
  2162. // anchor
  2163. //
  2164. ConsoleHideCursor(ScreenInfo);
  2165. #ifdef FE_SB
  2166. RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y) % ScreenInfo->ScreenBufferSize.Y;
  2167. Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
  2168. if (CONSOLE_IS_DBCS_OUTPUTCP(Console))
  2169. {
  2170. KAttrs = Row->CharRow.KAttrs[ScreenInfo->BufferInfo.TextInfo.CursorPosition.X];
  2171. if (KAttrs & ATTR_LEADING_BYTE)
  2172. NextRightX = 2;
  2173. else
  2174. NextRightX = 1;
  2175. }
  2176. else
  2177. {
  2178. NextRightX = 1;
  2179. }
  2180. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.X > 0) {
  2181. if (CONSOLE_IS_DBCS_OUTPUTCP(Console)) {
  2182. KAttrs = Row->CharRow.KAttrs[ScreenInfo->BufferInfo.TextInfo.CursorPosition.X-1];
  2183. if (KAttrs & ATTR_TRAILING_BYTE)
  2184. NextLeftX = 2;
  2185. else if (KAttrs & ATTR_LEADING_BYTE) {
  2186. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.X-1 > 0) {
  2187. KAttrs = Row->CharRow.KAttrs[ScreenInfo->BufferInfo.TextInfo.CursorPosition.X-2];
  2188. if (KAttrs & ATTR_TRAILING_BYTE)
  2189. NextLeftX = 3;
  2190. else
  2191. NextLeftX = 2;
  2192. }
  2193. else
  2194. NextLeftX = 1;
  2195. }
  2196. else
  2197. NextLeftX = 1;
  2198. }
  2199. else
  2200. NextLeftX = 1;
  2201. }
  2202. switch (VirtualKeyCode) {
  2203. case VK_RIGHT:
  2204. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.X+NextRightX < ScreenInfo->ScreenBufferSize.X) {
  2205. ScreenInfo->BufferInfo.TextInfo.CursorPosition.X+=NextRightX;
  2206. }
  2207. break;
  2208. case VK_LEFT:
  2209. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.X > 0) {
  2210. ScreenInfo->BufferInfo.TextInfo.CursorPosition.X-=NextLeftX;
  2211. }
  2212. break;
  2213. case VK_UP:
  2214. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y > 0) {
  2215. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y-=1;
  2216. }
  2217. break;
  2218. case VK_DOWN:
  2219. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y+1 < ScreenInfo->ScreenBufferSize.Y) {
  2220. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y+=1;
  2221. }
  2222. break;
  2223. case VK_NEXT:
  2224. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y += CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-1;
  2225. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y >= ScreenInfo->ScreenBufferSize.Y) {
  2226. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y = ScreenInfo->ScreenBufferSize.Y-1;
  2227. }
  2228. break;
  2229. case VK_PRIOR:
  2230. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y -= CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-1;
  2231. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y < 0) {
  2232. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y = 0;
  2233. }
  2234. break;
  2235. case VK_END:
  2236. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y = ScreenInfo->ScreenBufferSize.Y-CONSOLE_WINDOW_SIZE_Y(ScreenInfo);
  2237. break;
  2238. case VK_HOME:
  2239. ScreenInfo->BufferInfo.TextInfo.CursorPosition.X = 0;
  2240. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y = 0;
  2241. break;
  2242. default:
  2243. UserAssert(FALSE);
  2244. }
  2245. #else // FE_SB
  2246. switch (VirtualKeyCode) {
  2247. case VK_RIGHT:
  2248. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.X+1 < ScreenInfo->ScreenBufferSize.X) {
  2249. ScreenInfo->BufferInfo.TextInfo.CursorPosition.X+=1;
  2250. }
  2251. break;
  2252. case VK_LEFT:
  2253. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.X > 0) {
  2254. ScreenInfo->BufferInfo.TextInfo.CursorPosition.X-=1;
  2255. }
  2256. break;
  2257. case VK_UP:
  2258. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y > 0) {
  2259. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y-=1;
  2260. }
  2261. break;
  2262. case VK_DOWN:
  2263. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y+1 < ScreenInfo->ScreenBufferSize.Y) {
  2264. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y+=1;
  2265. }
  2266. break;
  2267. case VK_NEXT:
  2268. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y += CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-1;
  2269. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y >= ScreenInfo->ScreenBufferSize.Y) {
  2270. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y = ScreenInfo->ScreenBufferSize.Y-1;
  2271. }
  2272. break;
  2273. case VK_PRIOR:
  2274. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y -= CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-1;
  2275. if (ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y < 0) {
  2276. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y = 0;
  2277. }
  2278. break;
  2279. case VK_END:
  2280. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y = ScreenInfo->ScreenBufferSize.Y-CONSOLE_WINDOW_SIZE_Y(ScreenInfo);
  2281. break;
  2282. case VK_HOME:
  2283. ScreenInfo->BufferInfo.TextInfo.CursorPosition.X = 0;
  2284. ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y = 0;
  2285. break;
  2286. default:
  2287. UserAssert(FALSE);
  2288. }
  2289. #endif // FE_SB
  2290. ConsoleShowCursor(ScreenInfo);
  2291. if (GetKeyState(VK_SHIFT) & KEY_PRESSED) {
  2292. {
  2293. ExtendSelection(Console,ScreenInfo->BufferInfo.TextInfo.CursorPosition);
  2294. }
  2295. } else {
  2296. if (Console->SelectionFlags & CONSOLE_SELECTION_NOT_EMPTY) {
  2297. MyInvert(Console,&Console->SelectionRect);
  2298. Console->SelectionFlags &= ~CONSOLE_SELECTION_NOT_EMPTY;
  2299. ConsoleShowCursor(ScreenInfo);
  2300. }
  2301. ScreenInfo->BufferInfo.TextInfo.CursorMoved = TRUE;
  2302. Console->SelectionAnchor = ScreenInfo->BufferInfo.TextInfo.CursorPosition;
  2303. MakeCursorVisible(ScreenInfo,Console->SelectionAnchor);
  2304. Console->SelectionRect.Left = Console->SelectionRect.Right = Console->SelectionAnchor.X;
  2305. Console->SelectionRect.Top = Console->SelectionRect.Bottom = Console->SelectionAnchor.Y;
  2306. }
  2307. return;
  2308. }
  2309. } else if (!(Console->SelectionFlags & CONSOLE_MOUSE_DOWN)) {
  2310. //
  2311. // if in mouse selection mode and user hits a key, cancel selection
  2312. //
  2313. if (!IsSystemKey(VirtualKeyCode)) {
  2314. ClearSelection(Console);
  2315. }
  2316. }
  2317. } else if (Console->Flags & CONSOLE_SCROLLING) {
  2318. if (!bKeyDown) {
  2319. return;
  2320. }
  2321. //
  2322. // if escape, enter or ctrl-c, cancel scroll
  2323. //
  2324. if (VirtualKeyCode == VK_ESCAPE ||
  2325. VirtualKeyCode == VK_RETURN ||
  2326. (VirtualKeyCode == 'C' &&
  2327. (GetKeyState(VK_CONTROL) & KEY_PRESSED) )) {
  2328. ClearScroll(Console);
  2329. } else {
  2330. WORD ScrollCommand;
  2331. BOOL Horizontal=FALSE;
  2332. switch (VirtualKeyCode) {
  2333. case VK_UP:
  2334. ScrollCommand = SB_LINEUP;
  2335. break;
  2336. case VK_DOWN:
  2337. ScrollCommand = SB_LINEDOWN;
  2338. break;
  2339. case VK_LEFT:
  2340. ScrollCommand = SB_LINEUP;
  2341. Horizontal=TRUE;
  2342. break;
  2343. case VK_RIGHT:
  2344. ScrollCommand = SB_LINEDOWN;
  2345. Horizontal=TRUE;
  2346. break;
  2347. case VK_NEXT:
  2348. ScrollCommand = SB_PAGEDOWN;
  2349. break;
  2350. case VK_PRIOR:
  2351. ScrollCommand = SB_PAGEUP;
  2352. break;
  2353. case VK_END:
  2354. ScrollCommand = SB_PAGEDOWN;
  2355. Horizontal=TRUE;
  2356. break;
  2357. case VK_HOME:
  2358. ScrollCommand = SB_PAGEUP;
  2359. Horizontal=TRUE;
  2360. break;
  2361. case VK_SHIFT:
  2362. case VK_CONTROL:
  2363. case VK_MENU:
  2364. return;
  2365. default:
  2366. Beep(800, 200);
  2367. return;
  2368. }
  2369. if (Horizontal) {
  2370. HorizontalScroll(Console->CurrentScreenBuffer, ScrollCommand, 0);
  2371. } else {
  2372. VerticalScroll(Console, Console->CurrentScreenBuffer,ScrollCommand,0);
  2373. }
  2374. }
  2375. return;
  2376. }
  2377. //
  2378. // if the user is inputting chars at an inappropriate time, beep.
  2379. //
  2380. if ((Console->Flags & (CONSOLE_SELECTING | CONSOLE_SCROLLING | CONSOLE_SCROLLBAR_TRACKING)) &&
  2381. bKeyDown &&
  2382. !IsSystemKey(VirtualKeyCode)) {
  2383. Beep(800, 200);
  2384. return;
  2385. }
  2386. //
  2387. // if in fullscreen mode, process PrintScreen
  2388. //
  2389. #ifdef LATER
  2390. //
  2391. // Changed this code to get commas to work (build 485).
  2392. //
  2393. // Therese, the problem is that WM_CHAR/WM_SYSCHAR messages come through
  2394. // here - in this case, LOWORD(wParam) is a character value and not a virtual
  2395. // key. It happens that VK_SNAPSHOT == 0x2c, and the character value for a
  2396. // comma is also == 0x2c, so execution enters this conditional when a comma
  2397. // is hit. Commas aren't coming out because of the newly entered return
  2398. // statement.
  2399. //
  2400. // HandleKeyEvent() is making many virtual key comparisons - need to make
  2401. // sure that for each one, there is either no corresponding character value,
  2402. // or that you check before you compare so that you are comparing two values
  2403. // that have the same data type.
  2404. //
  2405. // I added the message comparison so that we know we're checking virtual
  2406. // keys against virtual keys and not characters.
  2407. //
  2408. // - scottlu
  2409. //
  2410. #endif
  2411. if (Message != WM_CHAR && Message != WM_SYSCHAR &&
  2412. VirtualKeyCode == VK_SNAPSHOT &&
  2413. !(Console->ReserveKeys & (CONSOLE_ALTPRTSC | CONSOLE_PRTSC )) ) {
  2414. if (Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
  2415. Console->SelectionFlags |= CONSOLE_SELECTION_NOT_EMPTY;
  2416. Console->SelectionRect = Console->CurrentScreenBuffer->Window;
  2417. StoreSelection(Console);
  2418. Console->SelectionFlags &= ~CONSOLE_SELECTION_NOT_EMPTY;
  2419. }
  2420. return;
  2421. }
  2422. //
  2423. // IME stuff
  2424. //
  2425. if (!(Console->Flags & CONSOLE_VDM_REGISTERED)) {
  2426. LPARAM lParamForHotKey ;
  2427. DWORD HotkeyID ;
  2428. lParamForHotKey = lParam ;
  2429. HotkeyID = NtUserCheckImeHotKey( (VirtualKeyCode & 0x00ff),lParamForHotKey) ;
  2430. //
  2431. // If it's direct KL switching hokey, handle it here
  2432. // regardless the system is IME enabled or not.
  2433. //
  2434. if (HotkeyID >= IME_HOTKEY_DSWITCH_FIRST && HotkeyID <= IME_HOTKEY_DSWITCH_LAST) {
  2435. UINT uModifier, uVkey;
  2436. HKL hkl;
  2437. RIPMSG1(RIP_VERBOSE, "HandleKeyEvent: handling IME HOTKEY id=%x", HotkeyID);
  2438. if (NtUserGetImeHotKey(HotkeyID, &uModifier, &uVkey, &hkl) && hkl != NULL) {
  2439. BYTE bCharSetSys = CodePageToCharSet(GetACP());
  2440. WPARAM wpSysChar = 0;
  2441. CHARSETINFO cs;
  2442. if (TranslateCharsetInfo((LPDWORD)LOWORD(hkl), &cs, TCI_SRCLOCALE)) {
  2443. if (bCharSetSys == cs.ciCharset) {
  2444. wpSysChar = INPUTLANGCHANGE_SYSCHARSET;
  2445. }
  2446. }
  2447. PostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST, wpSysChar, (LPARAM)hkl);
  2448. }
  2449. return;
  2450. }
  2451. if (!(Console->InputBuffer.ImeMode.Disable) && CONSOLE_IS_IME_ENABLED()) {
  2452. if (HotkeyID != IME_INVALID_HOTKEY) {
  2453. switch(HotkeyID) {
  2454. case IME_JHOTKEY_CLOSE_OPEN:
  2455. {
  2456. BOOL fOpen = Console->InputBuffer.ImeMode.Open;
  2457. if (!bKeyDown)
  2458. break ;
  2459. Console->InputBuffer.ImeMode.Open = !fOpen ;
  2460. if (!NT_SUCCESS(ConsoleImeMessagePump(Console,
  2461. CONIME_HOTKEY,
  2462. (WPARAM)Console->ConsoleHandle,
  2463. HotkeyID))) {
  2464. break;
  2465. }
  2466. // Update in the system conversion mode buffer.
  2467. GetImeKeyState(Console, NULL);
  2468. break ;
  2469. }
  2470. case IME_CHOTKEY_IME_NONIME_TOGGLE:
  2471. case IME_THOTKEY_IME_NONIME_TOGGLE:
  2472. case IME_CHOTKEY_SHAPE_TOGGLE:
  2473. case IME_THOTKEY_SHAPE_TOGGLE:
  2474. case IME_CHOTKEY_SYMBOL_TOGGLE:
  2475. case IME_THOTKEY_SYMBOL_TOGGLE:
  2476. case IME_KHOTKEY_SHAPE_TOGGLE:
  2477. case IME_KHOTKEY_HANJACONVERT:
  2478. case IME_KHOTKEY_ENGLISH:
  2479. case IME_ITHOTKEY_RESEND_RESULTSTR:
  2480. case IME_ITHOTKEY_PREVIOUS_COMPOSITION:
  2481. case IME_ITHOTKEY_UISTYLE_TOGGLE:
  2482. default:
  2483. {
  2484. if (!NT_SUCCESS(ConsoleImeMessagePump(Console,
  2485. CONIME_HOTKEY,
  2486. (WPARAM)Console->ConsoleHandle,
  2487. HotkeyID))) {
  2488. break;
  2489. }
  2490. // Update in the system conversion mode buffer.
  2491. GetImeKeyState(Console, NULL);
  2492. break ;
  2493. }
  2494. }
  2495. return ;
  2496. }
  2497. if ( CTRL_BUT_NOT_ALT(ControlKeyState) &&
  2498. (bKeyDown) ) {
  2499. if (VirtualKeyCode == 'C' &&
  2500. Console->InputBuffer.InputMode & ENABLE_PROCESSED_INPUT) {
  2501. goto FromConsoleIME ;
  2502. }
  2503. else if (VirtualKeyCode == VK_CANCEL) {
  2504. goto FromConsoleIME ;
  2505. }
  2506. else if (VirtualKeyCode == 'S'){
  2507. goto FromConsoleIME ;
  2508. }
  2509. }
  2510. else if (VirtualKeyCode == VK_PAUSE ){
  2511. goto FromConsoleIME ;
  2512. }
  2513. else if ( ((VirtualKeyCode == VK_SHIFT) ||
  2514. (VirtualKeyCode == VK_CONTROL) ||
  2515. (VirtualKeyCode == VK_CAPITAL) ||
  2516. (VirtualKeyCode == VK_KANA) || // VK_KANA == VK_HANGUL
  2517. (VirtualKeyCode == VK_JUNJA) ||
  2518. (VirtualKeyCode == VK_HANJA) ||
  2519. (VirtualKeyCode == VK_NUMLOCK) ||
  2520. (VirtualKeyCode == VK_SCROLL) )
  2521. &&
  2522. !(Console->InputBuffer.ImeMode.Unavailable) &&
  2523. !(Console->InputBuffer.ImeMode.Open)
  2524. )
  2525. {
  2526. if (!NT_SUCCESS(ConsoleImeMessagePump(Console,
  2527. Message+CONIME_KEYDATA,
  2528. (WPARAM)LOWORD(wParam)<<16|LOWORD(VirtualKeyCode),
  2529. lParam
  2530. ))) {
  2531. return;
  2532. }
  2533. goto FromConsoleIME ;
  2534. }
  2535. if (!Console->InputBuffer.ImeMode.Unavailable && Console->InputBuffer.ImeMode.Open) {
  2536. if (! (HIWORD(lParam) & KF_REPEAT))
  2537. {
  2538. if (PRIMARYLANGID(LOWORD(Console->hklActive)) == LANG_JAPANESE &&
  2539. (BYTE)wParam == VK_KANA) {
  2540. if (!NT_SUCCESS(ConsoleImeMessagePump(Console,
  2541. CONIME_NOTIFY_VK_KANA,
  2542. 0,
  2543. 0
  2544. ))) {
  2545. return;
  2546. }
  2547. }
  2548. }
  2549. ConsoleImeMessagePump(Console,
  2550. Message+CONIME_KEYDATA,
  2551. LOWORD(wParam)<<16|LOWORD(VirtualKeyCode),
  2552. lParam
  2553. );
  2554. return ;
  2555. }
  2556. }
  2557. }
  2558. FromConsoleIME:
  2559. //
  2560. // ignore key strokes that will generate CHAR messages. this is only
  2561. // necessary while a dialog box is up.
  2562. //
  2563. if (DialogBoxCount > 0) {
  2564. if (Message != WM_CHAR && Message != WM_SYSCHAR && Message != WM_DEADCHAR && Message != WM_SYSDEADCHAR) {
  2565. WCHAR awch[MAX_CHARS_FROM_1_KEYSTROKE];
  2566. int cwch;
  2567. BYTE KeyState[256];
  2568. GetKeyboardState(KeyState);
  2569. cwch = ToUnicodeEx((UINT)wParam,
  2570. HIWORD(lParam),
  2571. KeyState,
  2572. awch,
  2573. ARRAY_SIZE(awch),
  2574. TM_POSTCHARBREAKS,
  2575. NULL);
  2576. if (cwch != 0) {
  2577. return;
  2578. }
  2579. } else {
  2580. // remember to generate break
  2581. if (Message == WM_CHAR) {
  2582. bGenerateBreak = TRUE;
  2583. }
  2584. }
  2585. }
  2586. #ifdef FE_IME
  2587. // ignore key stroke while IME property is up.
  2588. if (Console->InputBuffer.hWndConsoleIME)
  2589. return;
  2590. #endif
  2591. InputEvent.EventType = KEY_EVENT;
  2592. InputEvent.Event.KeyEvent.bKeyDown = bKeyDown;
  2593. InputEvent.Event.KeyEvent.wRepeatCount = LOWORD(lParam);
  2594. if (Message == WM_CHAR || Message == WM_SYSCHAR || Message == WM_DEADCHAR || Message == WM_SYSDEADCHAR) {
  2595. // If this is a fake character, zero the scancode.
  2596. if (lParam & 0x02000000) {
  2597. InputEvent.Event.KeyEvent.wVirtualScanCode = 0;
  2598. }
  2599. InputEvent.Event.KeyEvent.dwControlKeyState = GetControlKeyState(lParam);
  2600. if (Message == WM_CHAR || Message == WM_SYSCHAR) {
  2601. InputEvent.Event.KeyEvent.uChar.UnicodeChar = (WCHAR)wParam;
  2602. } else {
  2603. InputEvent.Event.KeyEvent.uChar.UnicodeChar = (WCHAR)0;
  2604. }
  2605. } else {
  2606. // if alt-gr, ignore
  2607. if (lParam & 0x02000000) {
  2608. return;
  2609. }
  2610. InputEvent.Event.KeyEvent.dwControlKeyState = ControlKeyState;
  2611. InputEvent.Event.KeyEvent.uChar.UnicodeChar = 0;
  2612. }
  2613. #ifdef FE_IME
  2614. if (CONSOLE_IS_IME_ENABLED()) {
  2615. // MSKK August.22.1993 KazuM
  2616. DWORD dwConversion;
  2617. if (!NT_SUCCESS(GetImeKeyState(Console, &dwConversion))) {
  2618. return;
  2619. }
  2620. InputEvent.Event.KeyEvent.dwControlKeyState |= ImmConversionToConsole(dwConversion);
  2621. }
  2622. #endif
  2623. ContinueProcessing=TRUE;
  2624. if (CTRL_BUT_NOT_ALT(InputEvent.Event.KeyEvent.dwControlKeyState) &&
  2625. InputEvent.Event.KeyEvent.bKeyDown) {
  2626. //
  2627. // check for ctrl-c, if in line input mode.
  2628. //
  2629. if (InputEvent.Event.KeyEvent.wVirtualKeyCode == 'C' &&
  2630. Console->InputBuffer.InputMode & ENABLE_PROCESSED_INPUT) {
  2631. HandleCtrlEvent(Console,CTRL_C_EVENT);
  2632. if (!Console->PopupCount)
  2633. TerminateRead(Console,&Console->InputBuffer,CONSOLE_CTRL_C_SEEN);
  2634. if (!(Console->Flags & CONSOLE_SUSPENDED)) {
  2635. ContinueProcessing=FALSE;
  2636. }
  2637. }
  2638. //
  2639. // check for ctrl-break.
  2640. //
  2641. else if (InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_CANCEL) {
  2642. FlushInputBuffer(&Console->InputBuffer);
  2643. HandleCtrlEvent(Console,CTRL_BREAK_EVENT);
  2644. if (!Console->PopupCount)
  2645. TerminateRead(Console,&Console->InputBuffer,CONSOLE_CTRL_BREAK_SEEN);
  2646. if (!(Console->Flags & CONSOLE_SUSPENDED)) {
  2647. ContinueProcessing=FALSE;
  2648. }
  2649. }
  2650. //
  2651. // don't write ctrl-esc to the input buffer
  2652. //
  2653. else if (InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE &&
  2654. !(Console->ReserveKeys & CONSOLE_CTRLESC)) {
  2655. ContinueProcessing=FALSE;
  2656. }
  2657. } else if (InputEvent.Event.KeyEvent.dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) &&
  2658. InputEvent.Event.KeyEvent.bKeyDown &&
  2659. InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE &&
  2660. !(Console->ReserveKeys & CONSOLE_ALTESC)) {
  2661. ContinueProcessing=FALSE;
  2662. }
  2663. if (ContinueProcessing) {
  2664. EventsWritten = WriteInputBuffer( Console,
  2665. &Console->InputBuffer,
  2666. &InputEvent,
  2667. 1
  2668. );
  2669. if (EventsWritten && bGenerateBreak) {
  2670. InputEvent.Event.KeyEvent.bKeyDown = FALSE;
  2671. WriteInputBuffer( Console,
  2672. &Console->InputBuffer,
  2673. &InputEvent,
  2674. 1
  2675. );
  2676. }
  2677. }
  2678. }
  2679. /*
  2680. * Returns TRUE if DefWindowProc should be called.
  2681. */
  2682. BOOL
  2683. HandleMouseEvent(
  2684. IN PCONSOLE_INFORMATION Console,
  2685. IN PSCREEN_INFORMATION ScreenInfo,
  2686. IN UINT Message,
  2687. IN WPARAM wParam,
  2688. IN LPARAM lParam)
  2689. {
  2690. ULONG ButtonFlags,EventFlags;
  2691. INPUT_RECORD InputEvent;
  2692. ULONG EventsWritten;
  2693. COORD MousePosition;
  2694. SHORT RowIndex;
  2695. PROW Row;
  2696. if (!(Console->Flags & CONSOLE_HAS_FOCUS) &&
  2697. !(Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) &&
  2698. !(Console->SelectionFlags & CONSOLE_MOUSE_DOWN)) {
  2699. return TRUE;
  2700. }
  2701. if (Console->Flags & CONSOLE_IGNORE_NEXT_MOUSE_INPUT) {
  2702. // only reset on up transition
  2703. if (Message != WM_LBUTTONDOWN &&
  2704. Message != WM_MBUTTONDOWN &&
  2705. Message != WM_RBUTTONDOWN) {
  2706. Console->Flags &= ~CONSOLE_IGNORE_NEXT_MOUSE_INPUT;
  2707. return FALSE;
  2708. }
  2709. return TRUE;
  2710. }
  2711. //
  2712. // translate mouse position into characters, if necessary.
  2713. //
  2714. MousePosition.X = LOWORD(lParam);
  2715. MousePosition.Y = HIWORD(lParam);
  2716. if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
  2717. MousePosition.X /= SCR_FONTSIZE(ScreenInfo).X;
  2718. MousePosition.Y /= SCR_FONTSIZE(ScreenInfo).Y;
  2719. }
  2720. MousePosition.X += ScreenInfo->Window.Left;
  2721. MousePosition.Y += ScreenInfo->Window.Top;
  2722. //
  2723. // make sure mouse position is clipped to screen buffer
  2724. //
  2725. if (MousePosition.X < 0) {
  2726. MousePosition.X = 0;
  2727. } else if (MousePosition.X >= ScreenInfo->ScreenBufferSize.X) {
  2728. MousePosition.X = ScreenInfo->ScreenBufferSize.X - 1;
  2729. }
  2730. if (MousePosition.Y < 0) {
  2731. MousePosition.Y = 0;
  2732. } else if (MousePosition.Y >= ScreenInfo->ScreenBufferSize.Y) {
  2733. MousePosition.Y = ScreenInfo->ScreenBufferSize.Y - 1;
  2734. }
  2735. if (Console->Flags & CONSOLE_SELECTING ||
  2736. ((Console->Flags & CONSOLE_QUICK_EDIT_MODE) &&
  2737. (Console->FullScreenFlags == 0))) {
  2738. if (Message == WM_LBUTTONDOWN) {
  2739. //
  2740. // make sure message matches button state
  2741. //
  2742. if (!(GetKeyState(VK_LBUTTON) & KEY_PRESSED)) {
  2743. return FALSE;
  2744. }
  2745. if (Console->Flags & CONSOLE_QUICK_EDIT_MODE &&
  2746. !(Console->Flags & CONSOLE_SELECTING)) {
  2747. Console->Flags |= CONSOLE_SELECTING;
  2748. Console->SelectionFlags = CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN | CONSOLE_SELECTION_NOT_EMPTY;
  2749. //
  2750. // invert selection
  2751. //
  2752. InitializeMouseSelection(Console, MousePosition);
  2753. MyInvert(Console,&Console->SelectionRect);
  2754. SetWinText(Console,msgSelectMode,TRUE);
  2755. SetCapture(Console->hWnd);
  2756. } else {
  2757. //
  2758. // We now capture the mouse to our Window. We do this so that the
  2759. // user can "scroll" the selection endpoint to an off screen
  2760. // position by moving the mouse off the client area.
  2761. //
  2762. if (Console->SelectionFlags & CONSOLE_MOUSE_SELECTION) {
  2763. //
  2764. // Check for SHIFT-Mouse Down "continue previous selection"
  2765. // command.
  2766. //
  2767. if (GetKeyState(VK_SHIFT) & KEY_PRESSED) {
  2768. Console->SelectionFlags |= CONSOLE_MOUSE_DOWN;
  2769. SetCapture(Console->hWnd);
  2770. ExtendSelection(Console, MousePosition);
  2771. } else {
  2772. //
  2773. // Invert old selection, reset anchor, and invert
  2774. // new selection.
  2775. //
  2776. MyInvert(Console,&Console->SelectionRect);
  2777. Console->SelectionFlags |= CONSOLE_MOUSE_DOWN;
  2778. SetCapture(Console->hWnd);
  2779. InitializeMouseSelection(Console, MousePosition);
  2780. MyInvert(Console,&Console->SelectionRect);
  2781. }
  2782. } else {
  2783. ConvertToMouseSelect(Console, MousePosition);
  2784. }
  2785. }
  2786. } else if (Message == WM_LBUTTONUP) {
  2787. if (Console->SelectionFlags & CONSOLE_MOUSE_SELECTION) {
  2788. Console->SelectionFlags &= ~CONSOLE_MOUSE_DOWN;
  2789. ReleaseCapture();
  2790. }
  2791. } else if (Message == WM_LBUTTONDBLCLK) {
  2792. if ((MousePosition.X == Console->SelectionAnchor.X) &&
  2793. (MousePosition.Y == Console->SelectionAnchor.Y)) {
  2794. RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+MousePosition.Y) % ScreenInfo->ScreenBufferSize.Y;
  2795. Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
  2796. while (Console->SelectionAnchor.X > 0) {
  2797. if (IS_WORD_DELIM(Row->CharRow.Chars[Console->SelectionAnchor.X - 1])) {
  2798. break;
  2799. }
  2800. Console->SelectionAnchor.X--;
  2801. }
  2802. while (MousePosition.X < ScreenInfo->ScreenBufferSize.X) {
  2803. if (IS_WORD_DELIM(Row->CharRow.Chars[MousePosition.X])) {
  2804. break;
  2805. }
  2806. MousePosition.X++;
  2807. }
  2808. if (gfTrimLeadingZeros) {
  2809. //
  2810. // Trim the leading zeros: 000fe12 -> fe12, except 0x and 0n.
  2811. // Useful for debugging
  2812. //
  2813. if (MousePosition.X > Console->SelectionAnchor.X + 2 &&
  2814. Row->CharRow.Chars[Console->SelectionAnchor.X + 1] != L'x' &&
  2815. Row->CharRow.Chars[Console->SelectionAnchor.X + 1] != L'X' &&
  2816. Row->CharRow.Chars[Console->SelectionAnchor.X + 1] != L'n') {
  2817. // Don't touch the selection begins with 0x
  2818. while (Row->CharRow.Chars[Console->SelectionAnchor.X] == L'0' && Console->SelectionAnchor.X < MousePosition.X - 1) {
  2819. Console->SelectionAnchor.X++;
  2820. }
  2821. }
  2822. }
  2823. ExtendSelection(Console, MousePosition);
  2824. }
  2825. } else if ((Message == WM_RBUTTONDOWN) || (Message == WM_RBUTTONDBLCLK)) {
  2826. if (!(Console->SelectionFlags & CONSOLE_MOUSE_DOWN)) {
  2827. if (Console->Flags & CONSOLE_SELECTING) {
  2828. DoCopy(Console);
  2829. } else if (Console->Flags & CONSOLE_QUICK_EDIT_MODE) {
  2830. DoPaste(Console);
  2831. }
  2832. Console->Flags |= CONSOLE_IGNORE_NEXT_MOUSE_INPUT;
  2833. }
  2834. } else if (Message == WM_MOUSEMOVE) {
  2835. if (Console->SelectionFlags & CONSOLE_MOUSE_DOWN) {
  2836. ExtendSelection(Console, MousePosition);
  2837. }
  2838. } else if (Message == WM_MOUSEWHEEL) {
  2839. return TRUE;
  2840. }
  2841. return FALSE;
  2842. }
  2843. if (!(Console->InputBuffer.InputMode & ENABLE_MOUSE_INPUT)) {
  2844. ReleaseCapture();
  2845. if (Console->FullScreenFlags == 0) {
  2846. return TRUE;
  2847. }
  2848. return FALSE;
  2849. }
  2850. InputEvent.Event.MouseEvent.dwControlKeyState = GetControlKeyState(0);
  2851. if (Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
  2852. if (MousePosition.X > ScreenInfo->Window.Right) {
  2853. MousePosition.X = ScreenInfo->Window.Right;
  2854. }
  2855. if (MousePosition.Y > ScreenInfo->Window.Bottom) {
  2856. MousePosition.Y = ScreenInfo->Window.Bottom;
  2857. }
  2858. }
  2859. switch (Message) {
  2860. case WM_LBUTTONDOWN:
  2861. SetCapture(Console->hWnd);
  2862. ButtonFlags = FROM_LEFT_1ST_BUTTON_PRESSED;
  2863. EventFlags = 0;
  2864. break;
  2865. case WM_LBUTTONUP:
  2866. case WM_MBUTTONUP:
  2867. case WM_RBUTTONUP:
  2868. ReleaseCapture();
  2869. ButtonFlags = EventFlags = 0;
  2870. break;
  2871. case WM_RBUTTONDOWN:
  2872. SetCapture(Console->hWnd);
  2873. ButtonFlags = RIGHTMOST_BUTTON_PRESSED;
  2874. EventFlags = 0;
  2875. break;
  2876. case WM_MBUTTONDOWN:
  2877. SetCapture(Console->hWnd);
  2878. ButtonFlags = FROM_LEFT_2ND_BUTTON_PRESSED;
  2879. EventFlags = 0;
  2880. break;
  2881. case WM_MOUSEMOVE:
  2882. ButtonFlags = 0;
  2883. EventFlags = MOUSE_MOVED;
  2884. break;
  2885. case WM_LBUTTONDBLCLK:
  2886. ButtonFlags = FROM_LEFT_1ST_BUTTON_PRESSED;
  2887. EventFlags = DOUBLE_CLICK;
  2888. break;
  2889. case WM_RBUTTONDBLCLK:
  2890. ButtonFlags = RIGHTMOST_BUTTON_PRESSED;
  2891. EventFlags = DOUBLE_CLICK;
  2892. break;
  2893. case WM_MBUTTONDBLCLK:
  2894. ButtonFlags = FROM_LEFT_2ND_BUTTON_PRESSED;
  2895. EventFlags = DOUBLE_CLICK;
  2896. break;
  2897. case WM_MOUSEWHEEL:
  2898. ButtonFlags = ((UINT)wParam & 0xFFFF0000);
  2899. EventFlags = MOUSE_WHEELED;
  2900. break;
  2901. default:
  2902. RIPMSG1(RIP_ERROR, "Invalid message 0x%x", Message);
  2903. }
  2904. InputEvent.EventType = MOUSE_EVENT;
  2905. InputEvent.Event.MouseEvent.dwMousePosition = MousePosition;
  2906. InputEvent.Event.MouseEvent.dwEventFlags = EventFlags;
  2907. InputEvent.Event.MouseEvent.dwButtonState =
  2908. ConvertMouseButtonState(ButtonFlags, (UINT)wParam);
  2909. EventsWritten = WriteInputBuffer(Console,
  2910. &Console->InputBuffer,
  2911. &InputEvent,
  2912. 1
  2913. );
  2914. if (EventsWritten != 1) {
  2915. RIPMSG1(RIP_WARNING,
  2916. "PutInputInBuffer: EventsWritten != 1 (0x%x), 1 expected",
  2917. EventsWritten);
  2918. }
  2919. #ifdef i386
  2920. if (Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
  2921. UpdateMousePosition(ScreenInfo, InputEvent.Event.MouseEvent.dwMousePosition);
  2922. }
  2923. #endif
  2924. return FALSE;
  2925. }
  2926. VOID
  2927. HandleFocusEvent(
  2928. IN PCONSOLE_INFORMATION Console,
  2929. IN BOOL bSetFocus
  2930. )
  2931. {
  2932. INPUT_RECORD InputEvent;
  2933. ULONG EventsWritten;
  2934. USERTHREAD_FLAGS Flags;
  2935. InputEvent.EventType = FOCUS_EVENT;
  2936. InputEvent.Event.FocusEvent.bSetFocus = bSetFocus;
  2937. Flags.dwFlags = 0;
  2938. if (bSetFocus) {
  2939. if (Console->Flags & CONSOLE_VDM_REGISTERED) {
  2940. Flags.dwFlags |= TIF_VDMAPP;
  2941. }
  2942. if (Console->Flags & CONSOLE_CONNECTED_TO_EMULATOR) {
  2943. Flags.dwFlags |= TIF_DOSEMULATOR;
  2944. }
  2945. }
  2946. Flags.dwMask = (TIF_VDMAPP | TIF_DOSEMULATOR);
  2947. NtUserSetInformationThread(Console->InputThreadInfo->ThreadHandle,
  2948. UserThreadFlags, &Flags, sizeof(Flags));
  2949. EventsWritten = WriteInputBuffer( Console,
  2950. &Console->InputBuffer,
  2951. &InputEvent,
  2952. 1
  2953. );
  2954. #if DBG
  2955. if (EventsWritten != 1) {
  2956. DbgPrint("PutInputInBuffer: EventsWritten != 1, 1 expected\n");
  2957. }
  2958. #endif
  2959. }
  2960. VOID
  2961. HandleMenuEvent(
  2962. IN PCONSOLE_INFORMATION Console,
  2963. IN DWORD wParam
  2964. )
  2965. {
  2966. INPUT_RECORD InputEvent;
  2967. ULONG EventsWritten;
  2968. InputEvent.EventType = MENU_EVENT;
  2969. InputEvent.Event.MenuEvent.dwCommandId = wParam;
  2970. EventsWritten = WriteInputBuffer( Console,
  2971. &Console->InputBuffer,
  2972. &InputEvent,
  2973. 1
  2974. );
  2975. #if DBG
  2976. if (EventsWritten != 1) {
  2977. DbgPrint("PutInputInBuffer: EventsWritten != 1, 1 expected\n");
  2978. }
  2979. #endif
  2980. }
  2981. VOID
  2982. HandleCtrlEvent(
  2983. IN PCONSOLE_INFORMATION Console,
  2984. IN DWORD EventType
  2985. )
  2986. {
  2987. switch (EventType) {
  2988. case CTRL_C_EVENT:
  2989. Console->CtrlFlags |= CONSOLE_CTRL_C_FLAG;
  2990. break;
  2991. case CTRL_BREAK_EVENT:
  2992. Console->CtrlFlags |= CONSOLE_CTRL_BREAK_FLAG;
  2993. break;
  2994. case CTRL_CLOSE_EVENT:
  2995. Console->CtrlFlags |= CONSOLE_CTRL_CLOSE_FLAG;
  2996. break;
  2997. default:
  2998. RIPMSG1(RIP_ERROR, "Invalid EventType: 0x%x", EventType);
  2999. }
  3000. }
  3001. VOID
  3002. KillProcess(
  3003. PCONSOLE_PROCESS_TERMINATION_RECORD ProcessHandleRecord,
  3004. ULONG_PTR ProcessId
  3005. )
  3006. {
  3007. NTSTATUS status;
  3008. //
  3009. // Just terminate the process outright.
  3010. //
  3011. status = NtTerminateProcess(ProcessHandleRecord->ProcessHandle,
  3012. ProcessHandleRecord->bDebugee ? DBG_TERMINATE_PROCESS : CONTROL_C_EXIT);
  3013. #if DBG
  3014. if (status != STATUS_SUCCESS &&
  3015. status != STATUS_PROCESS_IS_TERMINATING &&
  3016. status != STATUS_THREAD_WAS_SUSPENDED &&
  3017. !(status == STATUS_ACCESS_DENIED && ProcessHandleRecord->bDebugee)) {
  3018. DbgPrint("NtTerminateProcess failed - status = %x\n", status);
  3019. DbgBreakPoint();
  3020. }
  3021. #endif
  3022. //
  3023. // Clear any remaining hard errors for the process.
  3024. //
  3025. if (ProcessId)
  3026. BoostHardError(ProcessId, BHE_FORCE);
  3027. //
  3028. // Give the process 5 seconds to exit.
  3029. //
  3030. if (NT_SUCCESS(status)) {
  3031. LARGE_INTEGER li;
  3032. li.QuadPart = (LONGLONG)-10000 * CMSHUNGAPPTIMEOUT;
  3033. status = NtWaitForSingleObject(ProcessHandleRecord->ProcessHandle,
  3034. FALSE,
  3035. &li);
  3036. if (status != STATUS_WAIT_0) {
  3037. RIPMSG2(RIP_WARNING,
  3038. "KillProcess: wait for process %x failed with status %x",
  3039. ProcessId, status);
  3040. }
  3041. }
  3042. }
  3043. int CreateCtrlThread(
  3044. IN PCONSOLE_PROCESS_TERMINATION_RECORD ProcessHandleList,
  3045. IN ULONG ProcessHandleListLength,
  3046. IN PWCHAR Title,
  3047. IN DWORD EventType,
  3048. IN BOOL fForce)
  3049. {
  3050. HANDLE Thread;
  3051. DWORD Status;
  3052. NTSTATUS status;
  3053. DWORD ShutdownFlags;
  3054. int Success=CONSOLE_SHUTDOWN_SUCCEEDED;
  3055. ULONG i;
  3056. DWORD EventFlags;
  3057. PROCESS_BASIC_INFORMATION BasicInfo;
  3058. PCSR_PROCESS Process;
  3059. BOOL fForceProcess;
  3060. BOOL fExitProcess;
  3061. BOOL fFirstPass=TRUE;
  3062. BOOL fSecondPassNeeded=FALSE;
  3063. BOOL fHasError;
  3064. BOOL fFirstWait;
  3065. BOOL fEventProcessed;
  3066. BOOL fBreakEvent;
  3067. BigLoop:
  3068. for (i = 0; i < ProcessHandleListLength; i++) {
  3069. //
  3070. // If the user has already cancelled shutdown, don't try to kill
  3071. // any more processes.
  3072. //
  3073. if (Success == CONSOLE_SHUTDOWN_FAILED) {
  3074. break;
  3075. }
  3076. //
  3077. // Get the process shutdown parameters here. First get the process
  3078. // id so we can get the csr process structure pointer.
  3079. //
  3080. status = NtQueryInformationProcess(ProcessHandleList[i].ProcessHandle,
  3081. ProcessBasicInformation,
  3082. &BasicInfo,
  3083. sizeof(BasicInfo),
  3084. NULL);
  3085. //
  3086. // Grab the shutdown flags from the csr process structure. If
  3087. // the structure cannot be found, terminate the process.
  3088. //
  3089. ProcessHandleList[i].bDebugee = FALSE;
  3090. ShutdownFlags = 0;
  3091. if (NT_SUCCESS(status)) {
  3092. CsrLockProcessByClientId((HANDLE)BasicInfo.UniqueProcessId, &Process);
  3093. if (Process == NULL) {
  3094. KillProcess(&ProcessHandleList[i], BasicInfo.UniqueProcessId);
  3095. continue;
  3096. }
  3097. } else {
  3098. KillProcess(&ProcessHandleList[i], 0);
  3099. continue;
  3100. }
  3101. ShutdownFlags = Process->ShutdownFlags;
  3102. ProcessHandleList[i].bDebugee = Process->DebugUserInterface.UniqueProcess!=NULL;
  3103. CsrUnlockProcess(Process);
  3104. if (!ProcessHandleList[i].bDebugee) {
  3105. HANDLE DebugPort = NULL;
  3106. //
  3107. // See if we're a console app that's being debugged.
  3108. //
  3109. status = NtQueryInformationProcess(
  3110. ProcessHandleList[i].ProcessHandle,
  3111. ProcessDebugPort,
  3112. (PVOID)&DebugPort,
  3113. sizeof(DebugPort),
  3114. NULL
  3115. );
  3116. if (NT_SUCCESS(status) && DebugPort) {
  3117. ProcessHandleList[i].bDebugee = TRUE;
  3118. }
  3119. }
  3120. if (EventType != CTRL_C_EVENT && EventType != CTRL_BREAK_EVENT) {
  3121. fBreakEvent = FALSE;
  3122. if (fFirstPass) {
  3123. if (ProcessHandleList[i].bDebugee) {
  3124. fSecondPassNeeded = TRUE;
  3125. continue;
  3126. }
  3127. } else {
  3128. if (!ProcessHandleList[i].bDebugee) {
  3129. continue;
  3130. }
  3131. }
  3132. } else {
  3133. fBreakEvent = TRUE;
  3134. fFirstPass=FALSE;
  3135. }
  3136. //
  3137. // fForce is whether ExitWindowsEx was called with EWX_FORCE.
  3138. // ShutdownFlags are the shutdown flags for this process. If
  3139. // either are force (noretry is the same as force), then force:
  3140. // which means if the app doesn't exit, don't bring up the retry
  3141. // dialog - just force it to exit right away.
  3142. //
  3143. fForceProcess = fForce || (ShutdownFlags & SHUTDOWN_NORETRY);
  3144. //
  3145. // Only notify system security and service context processes.
  3146. // Don't bring up retry dialogs for them.
  3147. //
  3148. fExitProcess = TRUE;
  3149. EventFlags = 0;
  3150. if (ShutdownFlags & (SHUTDOWN_SYSTEMCONTEXT | SHUTDOWN_OTHERCONTEXT)) {
  3151. //
  3152. // System context - make sure we don't cause it to exit, make
  3153. // sure we don't bring up retry dialogs.
  3154. //
  3155. fExitProcess = FALSE;
  3156. fForceProcess = TRUE;
  3157. //
  3158. // This EventFlag will be passed on down to the CtrlRoutine()
  3159. // on the client side. That way that side knows not to exit
  3160. // this process.
  3161. //
  3162. EventFlags = 0x80000000;
  3163. }
  3164. //
  3165. // Is this the first time we're waiting for this process to die?
  3166. //
  3167. fFirstWait = TRUE;
  3168. fEventProcessed = FALSE;
  3169. while (!fEventProcessed) {
  3170. DWORD ThreadExitCode;
  3171. DWORD ProcessExitCode;
  3172. DWORD cMsTimeout;
  3173. Thread = InternalCreateCallbackThread(ProcessHandleList[i].ProcessHandle,
  3174. (ULONG_PTR)ProcessHandleList[i].CtrlRoutine,
  3175. EventType | EventFlags);
  3176. //
  3177. // If the thread cannot be created, terminate the process.
  3178. //
  3179. if (Thread == NULL) {
  3180. RIPMSG1(RIP_WARNING,
  3181. "CreateRemoteThread failed 0x%x",
  3182. GetLastError());
  3183. break;
  3184. }
  3185. //
  3186. // Mark the event as processed.
  3187. //
  3188. fEventProcessed = TRUE;
  3189. /*
  3190. * if it's a ctrl-c or ctrl-break event, just close our
  3191. * handle to the thread. otherwise it's a close. wait
  3192. * for client-side thread to terminate.
  3193. */
  3194. if (EventType == CTRL_CLOSE_EVENT) {
  3195. cMsTimeout = gCmsHungAppTimeout;
  3196. } else if (EventType == CTRL_LOGOFF_EVENT) {
  3197. cMsTimeout = gCmsWaitToKillTimeout;
  3198. } else if (EventType == CTRL_SHUTDOWN_EVENT) {
  3199. //
  3200. // If we are shutting down services.exe, we need to look in the
  3201. // registry to see how long to wait.
  3202. //
  3203. if (fFirstWait && BasicInfo.UniqueProcessId == gdwServicesProcessId) {
  3204. cMsTimeout = gdwServicesWaitToKillTimeout;
  3205. } else {
  3206. cMsTimeout = gCmsWaitToKillTimeout;
  3207. }
  3208. } else {
  3209. CloseHandle(Thread);
  3210. fExitProcess = FALSE;
  3211. break;
  3212. }
  3213. while (TRUE) {
  3214. fHasError = BoostHardError(BasicInfo.UniqueProcessId,
  3215. (fForceProcess ? BHE_FORCE : BHE_ACTIVATE));
  3216. //
  3217. // Use a 1 second wait if there was a hard error, otherwise
  3218. // wait cMsTimeout ms.
  3219. //
  3220. Status = InternalWaitCancel(Thread,
  3221. (fHasError && fForceProcess) ? 1000 : cMsTimeout);
  3222. if (Status == WAIT_TIMEOUT) {
  3223. int Action;
  3224. //
  3225. // If there was a hard error, see if there is another one.
  3226. //
  3227. if (fHasError && fForceProcess) {
  3228. continue;
  3229. }
  3230. if (!fForceProcess) {
  3231. //
  3232. // we timed out in the handler. ask the user what
  3233. // to do.
  3234. //
  3235. DialogBoxCount++;
  3236. Action = ThreadShutdownNotify(WMCS_CONSOLE, (ULONG_PTR)Thread, (LPARAM)Title);
  3237. DialogBoxCount--;
  3238. //
  3239. // If the response is Cancel or EndTask, exit the loop.
  3240. // Otherwise retry the wait.
  3241. //
  3242. if (Action == TSN_USERSAYSCANCEL) {
  3243. Success = CONSOLE_SHUTDOWN_FAILED;
  3244. }
  3245. }
  3246. } else if (Status == 0) {
  3247. ThreadExitCode = 0;
  3248. GetExitCodeThread(Thread,&ThreadExitCode);
  3249. GetExitCodeProcess(ProcessHandleList[i].ProcessHandle,
  3250. &ProcessExitCode);
  3251. //
  3252. // if the app returned TRUE (event handled)
  3253. // notify the user and see if the app should
  3254. // be terminated anyway.
  3255. //
  3256. if (fHasError || (ThreadExitCode == EventType &&
  3257. ProcessExitCode == STILL_ACTIVE)) {
  3258. int Action;
  3259. if (!fForceProcess) {
  3260. //
  3261. // Wait for the process to exit. If it does exit,
  3262. // don't bring up the end task dialog.
  3263. //
  3264. Status = InternalWaitCancel(ProcessHandleList[i].ProcessHandle,
  3265. (fHasError || fFirstWait) ? 1000 : cMsTimeout);
  3266. if (Status == 0) {
  3267. //
  3268. // The process exited.
  3269. //
  3270. fExitProcess = FALSE;
  3271. } else if (Status == WAIT_TIMEOUT) {
  3272. DialogBoxCount++;
  3273. Action = ThreadShutdownNotify(WMCS_CONSOLE,
  3274. (ULONG_PTR)ProcessHandleList[i].ProcessHandle,
  3275. (LPARAM)Title);
  3276. DialogBoxCount--;
  3277. if (Action == TSN_USERSAYSCANCEL) {
  3278. Success = CONSOLE_SHUTDOWN_FAILED;
  3279. }
  3280. }
  3281. }
  3282. } else {
  3283. //
  3284. // The process exited.
  3285. //
  3286. fExitProcess = FALSE;
  3287. }
  3288. }
  3289. //
  3290. // If we get here, we know that all wait conditions have
  3291. // been satisfied. Time to finish with the process.
  3292. //
  3293. break;
  3294. }
  3295. CloseHandle(Thread);
  3296. }
  3297. //
  3298. // If the process is shutting down, mark it as terminated.
  3299. // This prevents the process from raising any hard error popups
  3300. // after we're done shutting it down.
  3301. //
  3302. if (!fBreakEvent &&
  3303. !(ShutdownFlags & (SHUTDOWN_SYSTEMCONTEXT | SHUTDOWN_OTHERCONTEXT)) &&
  3304. Success == CONSOLE_SHUTDOWN_SUCCEEDED) {
  3305. CsrLockProcessByClientId(
  3306. (HANDLE)BasicInfo.UniqueProcessId, &Process);
  3307. if (Process) {
  3308. Process->Flags |= CSR_PROCESS_TERMINATED;
  3309. CsrUnlockProcess(Process);
  3310. }
  3311. //
  3312. // Force the termination of the process if needed. Otherwise,
  3313. // acknowledge any remaining hard errors.
  3314. //
  3315. if (fExitProcess) {
  3316. KillProcess(&ProcessHandleList[i],
  3317. BasicInfo.UniqueProcessId);
  3318. } else {
  3319. BoostHardError(BasicInfo.UniqueProcessId, BHE_FORCE);
  3320. }
  3321. }
  3322. }
  3323. //
  3324. // If this was our first time through and we skipped one of the
  3325. // processes because it was being debugged, we'll go back for a
  3326. // second pass.
  3327. //
  3328. if (fFirstPass && fSecondPassNeeded) {
  3329. fFirstPass = FALSE;
  3330. goto BigLoop;
  3331. }
  3332. // if we're shutting down a system or service security context
  3333. // thread, don't wait for the process to terminate
  3334. if (ShutdownFlags & (SHUTDOWN_SYSTEMCONTEXT | SHUTDOWN_OTHERCONTEXT)) {
  3335. return CONSOLE_SHUTDOWN_SYSTEM;
  3336. }
  3337. return Success;
  3338. }
  3339. int
  3340. ProcessCtrlEvents(
  3341. IN PCONSOLE_INFORMATION Console
  3342. )
  3343. /* returns TRUE if a ctrl thread was created */
  3344. {
  3345. PWCHAR Title;
  3346. CONSOLE_PROCESS_TERMINATION_RECORD ProcessHandles[2];
  3347. PCONSOLE_PROCESS_TERMINATION_RECORD ProcessHandleList;
  3348. ULONG ProcessHandleListLength,i;
  3349. ULONG CtrlFlags;
  3350. PLIST_ENTRY ListHead, ListNext;
  3351. BOOL FreeTitle;
  3352. int Success;
  3353. PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
  3354. DWORD EventType;
  3355. DWORD LimitingProcessId;
  3356. NTSTATUS Status;
  3357. //
  3358. // If the console was marked for destruction, do it now.
  3359. //
  3360. if (Console->Flags & CONSOLE_IN_DESTRUCTION) {
  3361. DestroyConsole(Console);
  3362. return CONSOLE_SHUTDOWN_FAILED;
  3363. }
  3364. //
  3365. // make sure we don't try to process control events if this
  3366. // console is already going away
  3367. //
  3368. if (Console->Flags & CONSOLE_TERMINATING) {
  3369. Console->CtrlFlags = 0;
  3370. }
  3371. if (Console->CtrlFlags == 0) {
  3372. RtlLeaveCriticalSection(&Console->ConsoleLock);
  3373. return CONSOLE_SHUTDOWN_FAILED;
  3374. }
  3375. //
  3376. // make our own copy of the console process handle list
  3377. //
  3378. LimitingProcessId = Console->LimitingProcessId;
  3379. Console->LimitingProcessId = 0;
  3380. ListHead = &Console->ProcessHandleList;
  3381. ListNext = ListHead->Flink;
  3382. ProcessHandleListLength = 0;
  3383. while (ListNext != ListHead) {
  3384. ProcessHandleRecord = CONTAINING_RECORD( ListNext, CONSOLE_PROCESS_HANDLE, ListLink );
  3385. ListNext = ListNext->Flink;
  3386. if ( LimitingProcessId ) {
  3387. if ( ProcessHandleRecord->Process->ProcessGroupId == LimitingProcessId ) {
  3388. ProcessHandleListLength += 1;
  3389. }
  3390. } else {
  3391. ProcessHandleListLength += 1;
  3392. }
  3393. }
  3394. //
  3395. // Use the stack buffer to hold the process handles if there are only a
  3396. // few, otherwise allocate a buffer from the heap.
  3397. //
  3398. if (ProcessHandleListLength <= ARRAY_SIZE(ProcessHandles)) {
  3399. ProcessHandleList = ProcessHandles;
  3400. } else {
  3401. ProcessHandleList = ConsoleHeapAlloc(TMP_TAG, ProcessHandleListLength * sizeof(CONSOLE_PROCESS_TERMINATION_RECORD));
  3402. if (ProcessHandleList == NULL) {
  3403. RtlLeaveCriticalSection(&Console->ConsoleLock);
  3404. return CONSOLE_SHUTDOWN_FAILED;
  3405. }
  3406. }
  3407. ListNext = ListHead->Flink;
  3408. i = 0;
  3409. while (ListNext != ListHead) {
  3410. BOOLEAN ProcessIsIn;
  3411. UserAssert(i <= ProcessHandleListLength);
  3412. ProcessHandleRecord = CONTAINING_RECORD(ListNext, CONSOLE_PROCESS_HANDLE, ListLink);
  3413. ListNext = ListNext->Flink;
  3414. if (LimitingProcessId) {
  3415. if (ProcessHandleRecord->Process->ProcessGroupId == LimitingProcessId) {
  3416. ProcessIsIn = TRUE;
  3417. } else {
  3418. ProcessIsIn = FALSE;
  3419. }
  3420. } else {
  3421. ProcessIsIn = TRUE;
  3422. }
  3423. if (ProcessIsIn) {
  3424. Success = (int)DuplicateHandle(NtCurrentProcess(),
  3425. ProcessHandleRecord->ProcessHandle,
  3426. NtCurrentProcess(),
  3427. &ProcessHandleList[i].ProcessHandle,
  3428. 0,
  3429. FALSE,
  3430. DUPLICATE_SAME_ACCESS);
  3431. //
  3432. // If the duplicate failed, the best we can do is to skip
  3433. // including the process in the list and hope it goes
  3434. // away.
  3435. //
  3436. if (!Success) {
  3437. RIPMSG3(RIP_WARNING,
  3438. "Dup handle failed for %d of %d in 0x%p",
  3439. i,
  3440. ProcessHandleListLength,
  3441. Console);
  3442. continue;
  3443. }
  3444. if (Console->CtrlFlags & CONSOLE_CTRL_CLOSE_FLAG) {
  3445. ProcessHandleRecord->TerminateCount++;
  3446. } else {
  3447. ProcessHandleRecord->TerminateCount = 0;
  3448. }
  3449. ProcessHandleList[i].TerminateCount = ProcessHandleRecord->TerminateCount;
  3450. if (ProcessHandleRecord->CtrlRoutine) {
  3451. ProcessHandleList[i].CtrlRoutine = ProcessHandleRecord->CtrlRoutine;
  3452. } else {
  3453. ProcessHandleList[i].CtrlRoutine = CtrlRoutine;
  3454. }
  3455. //
  3456. // If this is the VDM process and we're closing the
  3457. // console window, move it to the front of the list
  3458. //
  3459. if (i > 0 && Console->VDMProcessId && Console->VDMProcessId ==
  3460. ProcessHandleRecord->Process->ClientId.UniqueProcess &&
  3461. ProcessHandleRecord->TerminateCount > 0) {
  3462. CONSOLE_PROCESS_TERMINATION_RECORD ProcessHandle;
  3463. ProcessHandle = ProcessHandleList[0];
  3464. ProcessHandleList[0] = ProcessHandleList[i];
  3465. ProcessHandleList[i] = ProcessHandle;
  3466. }
  3467. i++;
  3468. }
  3469. }
  3470. ProcessHandleListLength = i;
  3471. UserAssert(ProcessHandleListLength > 0);
  3472. //
  3473. // Copy title. titlelength does not include terminating null.
  3474. //
  3475. Title = ConsoleHeapAlloc(TITLE_TAG, Console->TitleLength + sizeof(WCHAR));
  3476. if (Title) {
  3477. FreeTitle = TRUE;
  3478. RtlCopyMemory(Title,Console->Title,Console->TitleLength+sizeof(WCHAR));
  3479. } else {
  3480. FreeTitle = FALSE;
  3481. Title = L"Command Window";
  3482. }
  3483. //
  3484. // Copy ctrl flags.
  3485. //
  3486. CtrlFlags = Console->CtrlFlags;
  3487. UserAssert( !((CtrlFlags & (CONSOLE_CTRL_CLOSE_FLAG | CONSOLE_CTRL_BREAK_FLAG | CONSOLE_CTRL_C_FLAG)) &&
  3488. (CtrlFlags & (CONSOLE_CTRL_LOGOFF_FLAG | CONSOLE_CTRL_SHUTDOWN_FLAG)) ));
  3489. Console->CtrlFlags = 0;
  3490. RtlLeaveCriticalSection(&Console->ConsoleLock);
  3491. //
  3492. // the ctrl flags could be a combination of the following
  3493. // values:
  3494. //
  3495. // CONSOLE_CTRL_C_FLAG
  3496. // CONSOLE_CTRL_BREAK_FLAG
  3497. // CONSOLE_CTRL_CLOSE_FLAG
  3498. // CONSOLE_CTRL_LOGOFF_FLAG
  3499. // CONSOLE_CTRL_SHUTDOWN_FLAG
  3500. //
  3501. Success = CONSOLE_SHUTDOWN_FAILED;
  3502. EventType = (DWORD)-1;
  3503. switch (CtrlFlags & (CONSOLE_CTRL_CLOSE_FLAG | CONSOLE_CTRL_BREAK_FLAG |
  3504. CONSOLE_CTRL_C_FLAG | CONSOLE_CTRL_LOGOFF_FLAG |
  3505. CONSOLE_CTRL_SHUTDOWN_FLAG)) {
  3506. case CONSOLE_CTRL_CLOSE_FLAG:
  3507. EventType = CTRL_CLOSE_EVENT;
  3508. break;
  3509. case CONSOLE_CTRL_BREAK_FLAG:
  3510. EventType = CTRL_BREAK_EVENT;
  3511. break;
  3512. case CONSOLE_CTRL_C_FLAG:
  3513. EventType = CTRL_C_EVENT;
  3514. break;
  3515. case CONSOLE_CTRL_LOGOFF_FLAG:
  3516. EventType = CTRL_LOGOFF_EVENT;
  3517. break;
  3518. case CONSOLE_CTRL_SHUTDOWN_FLAG:
  3519. EventType = CTRL_SHUTDOWN_EVENT;
  3520. break;
  3521. }
  3522. if (EventType != (DWORD)-1) {
  3523. Success = CreateCtrlThread(ProcessHandleList,
  3524. ProcessHandleListLength,
  3525. Title,
  3526. EventType,
  3527. (CtrlFlags & CONSOLE_FORCE_SHUTDOWN_FLAG) != 0
  3528. );
  3529. }
  3530. if (FreeTitle) {
  3531. ConsoleHeapFree(Title);
  3532. }
  3533. for (i=0;i<ProcessHandleListLength;i++) {
  3534. Status = NtClose(ProcessHandleList[i].ProcessHandle);
  3535. UserAssert(NT_SUCCESS(Status));
  3536. }
  3537. if (ProcessHandleList != ProcessHandles) {
  3538. ConsoleHeapFree(ProcessHandleList);
  3539. }
  3540. return Success;
  3541. }
  3542. VOID
  3543. UnlockConsole(
  3544. IN PCONSOLE_INFORMATION Console
  3545. )
  3546. {
  3547. LIST_ENTRY WaitQueue;
  3548. //
  3549. // Make sure the console pointer is still valid.
  3550. //
  3551. UserAssert(NT_SUCCESS(ValidateConsole(Console)));
  3552. #ifdef i386
  3553. //
  3554. // ALL the console locks locked by the UnlockConsoleOwningThread were
  3555. // released while it processing VDM screen switch. If it failed to get
  3556. // the locks back, the UnlockConsole has nothing to do.
  3557. //
  3558. if (Console->UnlockConsoleSkipCount != 0) {
  3559. if (Console->UnlockConsoleOwningThread == NtCurrentTeb()->ClientId.UniqueThread) {
  3560. Console->UnlockConsoleSkipCount--;
  3561. return;
  3562. }
  3563. }
  3564. //
  3565. // do nothing if we are in screen switching(handshaking with ntvdm)
  3566. // we don't check anything else because we are in a safe state here.
  3567. //
  3568. if (ConsoleVDMOnSwitching == Console &&
  3569. ConsoleVDMOnSwitching->VDMProcessId == CONSOLE_CLIENTPROCESSID()) {
  3570. RIPMSG1(RIP_WARNING,
  3571. " UnlockConsole - Thread %lx is leaving VDM CritSec",
  3572. GetCurrentThreadId());
  3573. RtlLeaveCriticalSection(&ConsoleVDMCriticalSection);
  3574. return;
  3575. }
  3576. #endif
  3577. //
  3578. // if we're about to release the console lock, see if there
  3579. // are any satisfied wait blocks that need to be dereferenced.
  3580. // this code avoids a deadlock between grabbing the console
  3581. // lock and then grabbing the process structure lock.
  3582. //
  3583. #if defined(_X86_) || defined(_AMD64_)
  3584. if (Console->ConsoleLock.RecursionCount == 1) {
  3585. #endif
  3586. #if defined(_IA64_)
  3587. if (Console->ConsoleLock.RecursionCount == 0) {
  3588. #endif
  3589. InitializeListHead(&WaitQueue);
  3590. if (Console->WaitQueue) {
  3591. CsrMoveSatisfiedWait(&WaitQueue, Console->WaitQueue);
  3592. Console->WaitQueue = NULL;
  3593. }
  3594. ProcessCtrlEvents(Console);
  3595. /*
  3596. * Can't call CsrDereferenceWait with the console locked or we could deadlock.
  3597. */
  3598. if (!IsListEmpty(&WaitQueue)) {
  3599. CsrDereferenceWait(&WaitQueue);
  3600. }
  3601. } else {
  3602. RtlLeaveCriticalSection(&Console->ConsoleLock);
  3603. }
  3604. }
  3605. ULONG
  3606. ShutdownConsole(
  3607. IN HANDLE ConsoleHandle,
  3608. IN DWORD dwFlags
  3609. )
  3610. /*
  3611. returns TRUE if console shutdown. we recurse here so we don't
  3612. return from the WM_QUERYENDSESSION until the console is gone.
  3613. */
  3614. {
  3615. DWORD EventFlag;
  3616. int WaitForShutdown;
  3617. PCONSOLE_INFORMATION Console;
  3618. EventFlag = 0;
  3619. //
  3620. // Transmit the force bit (meaning don't bring up the retry dialog
  3621. // if the app times out.
  3622. //
  3623. if (dwFlags & EWX_FORCE)
  3624. EventFlag |= CONSOLE_FORCE_SHUTDOWN_FLAG;
  3625. //
  3626. // Remember if this is shutdown or logoff - inquiring apps want to know.
  3627. //
  3628. if (dwFlags & EWX_SHUTDOWN) {
  3629. EventFlag |= CONSOLE_CTRL_SHUTDOWN_FLAG;
  3630. } else {
  3631. EventFlag |= CONSOLE_CTRL_LOGOFF_FLAG;
  3632. }
  3633. //
  3634. // see if console already going away
  3635. //
  3636. if (!NT_SUCCESS(RevalidateConsole(ConsoleHandle, &Console))) {
  3637. RIPMSG0(RIP_WARNING, "Shutting down terminating console");
  3638. return SHUTDOWN_KNOWN_PROCESS;
  3639. }
  3640. Console->Flags |= CONSOLE_SHUTTING_DOWN;
  3641. Console->CtrlFlags = EventFlag;
  3642. Console->LimitingProcessId = 0;
  3643. WaitForShutdown = ProcessCtrlEvents(Console);
  3644. if (WaitForShutdown == CONSOLE_SHUTDOWN_SUCCEEDED) {
  3645. return (ULONG)STATUS_PROCESS_IS_TERMINATING;
  3646. } else {
  3647. if (!NT_SUCCESS(RevalidateConsole(ConsoleHandle, &Console))) {
  3648. return SHUTDOWN_KNOWN_PROCESS;
  3649. }
  3650. Console->Flags &= ~CONSOLE_SHUTTING_DOWN;
  3651. UnlockConsole(Console);
  3652. if (WaitForShutdown == CONSOLE_SHUTDOWN_SYSTEM) {
  3653. return SHUTDOWN_KNOWN_PROCESS;
  3654. } else {
  3655. return SHUTDOWN_CANCEL;
  3656. }
  3657. }
  3658. }
  3659. /*
  3660. * Exit routine for threads created with RtlCreateUserThread. These threads
  3661. * cannot call ExitThread(). So don't do that.
  3662. *
  3663. */
  3664. VOID UserExitWorkerThread(
  3665. NTSTATUS Status)
  3666. {
  3667. NtCurrentTeb()->FreeStackOnTermination = TRUE;
  3668. NtTerminateThread(NtCurrentThread(), Status);
  3669. }