1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Console Driver DLL 4 * FILE: win32ss/user/winsrv/consrv/condrv/coninput.c 5 * PURPOSE: Console Input functions 6 * PROGRAMMERS: Jeffrey Morlan 7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 8 */ 9 10 /* INCLUDES *******************************************************************/ 11 12 #include <consrv.h> 13 14 #define NDEBUG 15 #include <debug.h> 16 17 /* PRIVATE FUNCTIONS **********************************************************/ 18 19 // ConDrvAddInputEvents 20 static NTSTATUS 21 AddInputEvents(PCONSOLE Console, 22 PINPUT_RECORD InputRecords, // InputEvent 23 ULONG NumEventsToWrite, 24 PULONG NumEventsWritten, 25 BOOLEAN AppendToEnd) 26 { 27 NTSTATUS Status = STATUS_SUCCESS; 28 ULONG i = 0; 29 BOOLEAN SetWaitEvent = FALSE; 30 31 if (NumEventsWritten) *NumEventsWritten = 0; 32 33 /* 34 * When adding many single events, in the case of repeated mouse move or 35 * key down events, we try to coalesce them so that we do not saturate 36 * too quickly the input buffer. 37 */ 38 if (NumEventsToWrite == 1 && !IsListEmpty(&Console->InputBuffer.InputEvents)) 39 { 40 PINPUT_RECORD InputRecord = InputRecords; // Only one element 41 PINPUT_RECORD LastInputRecord; 42 ConsoleInput* ConInRec; // Input 43 44 /* Get the "next" event of the input buffer */ 45 if (AppendToEnd) 46 { 47 /* Get the tail element */ 48 ConInRec = CONTAINING_RECORD(Console->InputBuffer.InputEvents.Blink, 49 ConsoleInput, ListEntry); 50 } 51 else 52 { 53 /* Get the head element */ 54 ConInRec = CONTAINING_RECORD(Console->InputBuffer.InputEvents.Flink, 55 ConsoleInput, ListEntry); 56 } 57 LastInputRecord = &ConInRec->InputEvent; 58 59 if (InputRecord->EventType == MOUSE_EVENT && 60 InputRecord->Event.MouseEvent.dwEventFlags == MOUSE_MOVED) 61 { 62 if (LastInputRecord->EventType == MOUSE_EVENT && 63 LastInputRecord->Event.MouseEvent.dwEventFlags == MOUSE_MOVED) 64 { 65 /* Update the mouse position */ 66 LastInputRecord->Event.MouseEvent.dwMousePosition.X = 67 InputRecord->Event.MouseEvent.dwMousePosition.X; 68 LastInputRecord->Event.MouseEvent.dwMousePosition.Y = 69 InputRecord->Event.MouseEvent.dwMousePosition.Y; 70 71 i = 1; 72 // return STATUS_SUCCESS; 73 Status = STATUS_SUCCESS; 74 } 75 } 76 else if (InputRecord->EventType == KEY_EVENT && 77 InputRecord->Event.KeyEvent.bKeyDown) 78 { 79 if (LastInputRecord->EventType == KEY_EVENT && 80 LastInputRecord->Event.KeyEvent.bKeyDown && 81 (LastInputRecord->Event.KeyEvent.wVirtualScanCode == // Same scancode 82 InputRecord->Event.KeyEvent.wVirtualScanCode) && 83 (LastInputRecord->Event.KeyEvent.uChar.UnicodeChar == // Same character 84 InputRecord->Event.KeyEvent.uChar.UnicodeChar) && 85 (LastInputRecord->Event.KeyEvent.dwControlKeyState == // Same Ctrl/Alt/Shift state 86 InputRecord->Event.KeyEvent.dwControlKeyState) ) 87 { 88 /* Update the repeat count */ 89 LastInputRecord->Event.KeyEvent.wRepeatCount += 90 InputRecord->Event.KeyEvent.wRepeatCount; 91 92 i = 1; 93 // return STATUS_SUCCESS; 94 Status = STATUS_SUCCESS; 95 } 96 } 97 } 98 99 /* If we coalesced the only one element, we can quit */ 100 if (i == 1 && Status == STATUS_SUCCESS /* && NumEventsToWrite == 1 */) 101 goto Done; 102 103 /* 104 * No event coalesced, add them in the usual way. 105 */ 106 107 if (AppendToEnd) 108 { 109 /* Go to the beginning of the list */ 110 // InputRecords = InputRecords; 111 } 112 else 113 { 114 /* Go to the end of the list */ 115 InputRecords = &InputRecords[NumEventsToWrite - 1]; 116 } 117 118 /* Set the event if the list is going to be non-empty */ 119 if (IsListEmpty(&Console->InputBuffer.InputEvents)) 120 SetWaitEvent = TRUE; 121 122 for (i = 0; i < NumEventsToWrite && NT_SUCCESS(Status); ++i) 123 { 124 PINPUT_RECORD InputRecord; 125 ConsoleInput* ConInRec; 126 127 if (AppendToEnd) 128 { 129 /* Select the event and go to the next one */ 130 InputRecord = InputRecords++; 131 } 132 else 133 { 134 /* Select the event and go to the previous one */ 135 InputRecord = InputRecords--; 136 } 137 138 /* Add event to the queue */ 139 ConInRec = ConsoleAllocHeap(0, sizeof(ConsoleInput)); 140 if (ConInRec == NULL) 141 { 142 // return STATUS_INSUFFICIENT_RESOURCES; 143 Status = STATUS_INSUFFICIENT_RESOURCES; 144 continue; 145 } 146 147 ConInRec->InputEvent = *InputRecord; 148 149 if (AppendToEnd) 150 { 151 /* Append the event to the end of the queue */ 152 InsertTailList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry); 153 } 154 else 155 { 156 /* Append the event to the beginning of the queue */ 157 InsertHeadList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry); 158 } 159 160 // return STATUS_SUCCESS; 161 Status = STATUS_SUCCESS; 162 } 163 164 if (SetWaitEvent) NtSetEvent(Console->InputBuffer.ActiveEvent, NULL); 165 166 Done: 167 if (NumEventsWritten) *NumEventsWritten = i; 168 169 return Status; 170 } 171 172 static VOID 173 PurgeInputBuffer(PCONSOLE Console) 174 { 175 PLIST_ENTRY CurrentEntry; 176 ConsoleInput* Event; 177 178 while (!IsListEmpty(&Console->InputBuffer.InputEvents)) 179 { 180 CurrentEntry = RemoveHeadList(&Console->InputBuffer.InputEvents); 181 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); 182 ConsoleFreeHeap(Event); 183 } 184 185 // CloseHandle(Console->InputBuffer.ActiveEvent); 186 } 187 188 NTSTATUS NTAPI 189 ConDrvInitInputBuffer(IN PCONSOLE Console, 190 IN ULONG InputBufferSize) 191 { 192 NTSTATUS Status; 193 OBJECT_ATTRIBUTES ObjectAttributes; 194 195 ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console); 196 197 InitializeObjectAttributes(&ObjectAttributes, 198 NULL, 199 OBJ_INHERIT, 200 NULL, 201 NULL); 202 203 Status = NtCreateEvent(&Console->InputBuffer.ActiveEvent, EVENT_ALL_ACCESS, 204 &ObjectAttributes, NotificationEvent, FALSE); 205 if (!NT_SUCCESS(Status)) 206 return Status; 207 208 Console->InputBuffer.InputBufferSize = InputBufferSize; 209 InitializeListHead(&Console->InputBuffer.InputEvents); 210 Console->InputBuffer.Mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | 211 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT; 212 213 return STATUS_SUCCESS; 214 } 215 216 VOID NTAPI 217 ConDrvDeinitInputBuffer(IN PCONSOLE Console) 218 { 219 PurgeInputBuffer(Console); 220 CloseHandle(Console->InputBuffer.ActiveEvent); 221 } 222 223 224 /* PUBLIC DRIVER APIS *********************************************************/ 225 226 NTSTATUS NTAPI 227 ConDrvReadConsole(IN PCONSOLE Console, 228 IN PCONSOLE_INPUT_BUFFER InputBuffer, 229 IN BOOLEAN Unicode, 230 OUT PVOID Buffer, 231 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl, 232 IN PVOID Parameter OPTIONAL, 233 IN ULONG NumCharsToRead, 234 OUT PULONG NumCharsRead OPTIONAL) 235 { 236 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait. 237 // NTSTATUS Status; = STATUS_PENDING; 238 239 if (Console == NULL || InputBuffer == NULL || /* Buffer == NULL || */ 240 ReadControl == NULL || ReadControl->nLength != sizeof(CONSOLE_READCONSOLE_CONTROL)) 241 { 242 return STATUS_INVALID_PARAMETER; 243 } 244 245 /* Validity checks */ 246 ASSERT(Console == InputBuffer->Header.Console); 247 ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0)); 248 249 /* Call the line-discipline */ 250 return TermReadStream(Console, 251 Unicode, 252 Buffer, 253 ReadControl, 254 Parameter, 255 NumCharsToRead, 256 NumCharsRead); 257 } 258 259 NTSTATUS NTAPI 260 ConDrvGetConsoleInput(IN PCONSOLE Console, 261 IN PCONSOLE_INPUT_BUFFER InputBuffer, 262 IN BOOLEAN KeepEvents, 263 IN BOOLEAN WaitForMoreEvents, 264 OUT PINPUT_RECORD InputRecord, 265 IN ULONG NumEventsToRead, 266 OUT PULONG NumEventsRead OPTIONAL) 267 { 268 PLIST_ENTRY CurrentInput; 269 ConsoleInput* Input; 270 ULONG i = 0; 271 272 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */) 273 return STATUS_INVALID_PARAMETER; 274 275 /* Validity checks */ 276 ASSERT(Console == InputBuffer->Header.Console); 277 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToRead == 0)); 278 279 if (NumEventsRead) *NumEventsRead = 0; 280 281 if (IsListEmpty(&InputBuffer->InputEvents)) 282 { 283 /* 284 * No input is available. Wait for more input if requested, 285 * otherwise, we don't wait, so we return success. 286 */ 287 return (WaitForMoreEvents ? STATUS_PENDING : STATUS_SUCCESS); 288 } 289 290 /* Only get input if there is any */ 291 CurrentInput = InputBuffer->InputEvents.Flink; 292 i = 0; 293 while ((CurrentInput != &InputBuffer->InputEvents) && (i < NumEventsToRead)) 294 { 295 Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry); 296 297 *InputRecord = Input->InputEvent; 298 299 ++InputRecord; 300 ++i; 301 CurrentInput = CurrentInput->Flink; 302 303 /* Remove the events from the queue if needed */ 304 if (!KeepEvents) 305 { 306 RemoveEntryList(&Input->ListEntry); 307 ConsoleFreeHeap(Input); 308 } 309 } 310 311 if (NumEventsRead) *NumEventsRead = i; 312 313 if (IsListEmpty(&InputBuffer->InputEvents)) 314 { 315 ResetEvent(InputBuffer->ActiveEvent); 316 } 317 318 // FIXME: If we add back UNICODE support, it's here that we need to do the translation. 319 320 /* We read all the inputs available, we return success */ 321 return STATUS_SUCCESS; 322 } 323 324 NTSTATUS NTAPI 325 ConDrvWriteConsoleInput(IN PCONSOLE Console, 326 IN PCONSOLE_INPUT_BUFFER InputBuffer, 327 IN BOOLEAN AppendToEnd, 328 IN PINPUT_RECORD InputRecord, 329 IN ULONG NumEventsToWrite, 330 OUT PULONG NumEventsWritten OPTIONAL) 331 { 332 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */) 333 return STATUS_INVALID_PARAMETER; 334 335 /* Validity checks */ 336 ASSERT(Console == InputBuffer->Header.Console); 337 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToWrite == 0)); 338 339 /* Now, add the events */ 340 if (NumEventsWritten) *NumEventsWritten = 0; 341 342 // FIXME: If we add back UNICODE support, it's here that we need to do the translation. 343 344 return AddInputEvents(Console, 345 InputRecord, 346 NumEventsToWrite, 347 NumEventsWritten, 348 AppendToEnd); 349 } 350 351 NTSTATUS NTAPI 352 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console, 353 IN PCONSOLE_INPUT_BUFFER InputBuffer) 354 { 355 PLIST_ENTRY CurrentEntry; 356 ConsoleInput* Event; 357 358 if (Console == NULL || InputBuffer == NULL) 359 return STATUS_INVALID_PARAMETER; 360 361 /* Validity check */ 362 ASSERT(Console == InputBuffer->Header.Console); 363 364 /* Discard all entries in the input event queue */ 365 while (!IsListEmpty(&InputBuffer->InputEvents)) 366 { 367 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents); 368 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); 369 ConsoleFreeHeap(Event); 370 } 371 ResetEvent(InputBuffer->ActiveEvent); 372 373 return STATUS_SUCCESS; 374 } 375 376 NTSTATUS NTAPI 377 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console, 378 IN PCONSOLE_INPUT_BUFFER InputBuffer, 379 OUT PULONG NumberOfEvents) 380 { 381 PLIST_ENTRY CurrentInput; 382 383 if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL) 384 return STATUS_INVALID_PARAMETER; 385 386 /* Validity check */ 387 ASSERT(Console == InputBuffer->Header.Console); 388 389 *NumberOfEvents = 0; 390 391 /* If there are any events ... */ 392 CurrentInput = InputBuffer->InputEvents.Flink; 393 while (CurrentInput != &InputBuffer->InputEvents) 394 { 395 CurrentInput = CurrentInput->Flink; 396 (*NumberOfEvents)++; 397 } 398 399 return STATUS_SUCCESS; 400 } 401 402 /* EOF */ 403