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 _InterlockedIncrement((PLONG)&Console->InputBuffer.NumberOfEvents); 160 161 // return STATUS_SUCCESS; 162 Status = STATUS_SUCCESS; 163 } 164 165 if (SetWaitEvent) NtSetEvent(Console->InputBuffer.ActiveEvent, NULL); 166 167 Done: 168 if (NumEventsWritten) *NumEventsWritten = i; 169 170 return Status; 171 } 172 173 static VOID 174 PurgeInputBuffer(IN PCONSOLE_INPUT_BUFFER InputBuffer) 175 { 176 PLIST_ENTRY CurrentEntry; 177 ConsoleInput* Event; 178 179 /* Discard all entries in the input event queue */ 180 _InterlockedExchange((PLONG)&InputBuffer->NumberOfEvents, 0); 181 while (!IsListEmpty(&InputBuffer->InputEvents)) 182 { 183 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents); 184 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); 185 ConsoleFreeHeap(Event); 186 } 187 188 // NtClose(Console->InputBuffer.ActiveEvent); 189 } 190 191 NTSTATUS NTAPI 192 ConDrvInitInputBuffer(IN PCONSOLE Console, 193 IN ULONG InputBufferSize) 194 { 195 NTSTATUS Status; 196 OBJECT_ATTRIBUTES ObjectAttributes; 197 198 ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console); 199 200 InitializeObjectAttributes(&ObjectAttributes, 201 NULL, 202 OBJ_INHERIT, 203 NULL, 204 NULL); 205 206 Status = NtCreateEvent(&Console->InputBuffer.ActiveEvent, EVENT_ALL_ACCESS, 207 &ObjectAttributes, NotificationEvent, FALSE); 208 if (!NT_SUCCESS(Status)) 209 return Status; 210 211 Console->InputBuffer.InputBufferSize = InputBufferSize; 212 Console->InputBuffer.NumberOfEvents = 0; 213 InitializeListHead(&Console->InputBuffer.InputEvents); 214 Console->InputBuffer.Mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | 215 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT; 216 217 return STATUS_SUCCESS; 218 } 219 220 VOID NTAPI 221 ConDrvDeinitInputBuffer(IN PCONSOLE Console) 222 { 223 PurgeInputBuffer(&Console->InputBuffer); 224 NtClose(Console->InputBuffer.ActiveEvent); 225 } 226 227 228 /* PUBLIC DRIVER APIS *********************************************************/ 229 230 NTSTATUS NTAPI 231 ConDrvReadConsole(IN PCONSOLE Console, 232 IN PCONSOLE_INPUT_BUFFER InputBuffer, 233 IN BOOLEAN Unicode, 234 OUT PVOID Buffer, 235 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl, 236 IN PVOID Parameter OPTIONAL, 237 IN ULONG NumCharsToRead, 238 OUT PULONG NumCharsRead OPTIONAL) 239 { 240 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait. 241 // NTSTATUS Status; = STATUS_PENDING; 242 243 if (Console == NULL || InputBuffer == NULL || /* Buffer == NULL || */ 244 ReadControl == NULL || ReadControl->nLength != sizeof(CONSOLE_READCONSOLE_CONTROL)) 245 { 246 return STATUS_INVALID_PARAMETER; 247 } 248 249 /* Validity checks */ 250 ASSERT(Console == InputBuffer->Header.Console); 251 ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0)); 252 253 /* Call the line-discipline */ 254 return TermReadStream(Console, 255 Unicode, 256 Buffer, 257 ReadControl, 258 Parameter, 259 NumCharsToRead, 260 NumCharsRead); 261 } 262 263 NTSTATUS NTAPI 264 ConDrvGetConsoleInput(IN PCONSOLE Console, 265 IN PCONSOLE_INPUT_BUFFER InputBuffer, 266 IN BOOLEAN KeepEvents, 267 IN BOOLEAN WaitForMoreEvents, 268 OUT PINPUT_RECORD InputRecord, 269 IN ULONG NumEventsToRead, 270 OUT PULONG NumEventsRead OPTIONAL) 271 { 272 PLIST_ENTRY CurrentInput; 273 ConsoleInput* Input; 274 ULONG i = 0; 275 276 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */) 277 return STATUS_INVALID_PARAMETER; 278 279 /* Validity checks */ 280 ASSERT(Console == InputBuffer->Header.Console); 281 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToRead == 0)); 282 283 if (NumEventsRead) *NumEventsRead = 0; 284 285 if (IsListEmpty(&InputBuffer->InputEvents)) 286 { 287 /* 288 * No input is available. Wait for more input if requested, 289 * otherwise, we don't wait, so we return success. 290 */ 291 return (WaitForMoreEvents ? STATUS_PENDING : STATUS_SUCCESS); 292 } 293 294 /* Only get input if there is any */ 295 CurrentInput = InputBuffer->InputEvents.Flink; 296 i = 0; 297 while ((CurrentInput != &InputBuffer->InputEvents) && (i < NumEventsToRead)) 298 { 299 Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry); 300 301 *InputRecord = Input->InputEvent; 302 303 ++InputRecord; 304 ++i; 305 CurrentInput = CurrentInput->Flink; 306 307 /* Remove the events from the queue if needed */ 308 if (!KeepEvents) 309 { 310 _InterlockedDecrement((PLONG)&InputBuffer->NumberOfEvents); 311 RemoveEntryList(&Input->ListEntry); 312 ConsoleFreeHeap(Input); 313 } 314 } 315 316 if (NumEventsRead) *NumEventsRead = i; 317 318 if (IsListEmpty(&InputBuffer->InputEvents)) 319 { 320 NtClearEvent(InputBuffer->ActiveEvent); 321 } 322 323 // FIXME: If we add back UNICODE support, it's here that we need to do the translation. 324 325 /* We read all the inputs available, we return success */ 326 return STATUS_SUCCESS; 327 } 328 329 NTSTATUS NTAPI 330 ConDrvWriteConsoleInput(IN PCONSOLE Console, 331 IN PCONSOLE_INPUT_BUFFER InputBuffer, 332 IN BOOLEAN AppendToEnd, 333 IN PINPUT_RECORD InputRecord, 334 IN ULONG NumEventsToWrite, 335 OUT PULONG NumEventsWritten OPTIONAL) 336 { 337 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */) 338 return STATUS_INVALID_PARAMETER; 339 340 /* Validity checks */ 341 ASSERT(Console == InputBuffer->Header.Console); 342 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToWrite == 0)); 343 344 /* Now, add the events */ 345 if (NumEventsWritten) *NumEventsWritten = 0; 346 347 // FIXME: If we add back UNICODE support, it's here that we need to do the translation. 348 349 return AddInputEvents(Console, 350 InputRecord, 351 NumEventsToWrite, 352 NumEventsWritten, 353 AppendToEnd); 354 } 355 356 NTSTATUS NTAPI 357 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console, 358 IN PCONSOLE_INPUT_BUFFER InputBuffer) 359 { 360 if (Console == NULL || InputBuffer == NULL) 361 return STATUS_INVALID_PARAMETER; 362 363 /* Validity check */ 364 ASSERT(Console == InputBuffer->Header.Console); 365 366 /* Discard all entries in the input event queue */ 367 PurgeInputBuffer(InputBuffer); 368 NtClearEvent(InputBuffer->ActiveEvent); 369 370 return STATUS_SUCCESS; 371 } 372 373 NTSTATUS NTAPI 374 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console, 375 IN PCONSOLE_INPUT_BUFFER InputBuffer, 376 OUT PULONG NumberOfEvents) 377 { 378 if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL) 379 return STATUS_INVALID_PARAMETER; 380 381 /* Validity check */ 382 ASSERT(Console == InputBuffer->Header.Console); 383 384 *NumberOfEvents = InputBuffer->NumberOfEvents; 385 return STATUS_SUCCESS; 386 } 387 388 /* EOF */ 389