Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Instance Methods | List of all members
FlutterEmbedderKeyResponderTest Class Reference
Inheritance diagram for FlutterEmbedderKeyResponderTest:

Instance Methods

(void) - setUp [implementation]
 
(void) - tearDown [implementation]
 
() - ios [implementation]
 
() - ios [implementation]
 
() - ios [implementation]
 
() - ios [implementation]
 
() - ios [implementation]
 
() - ios [implementation]
 
() - ios [implementation]
 
() - ios [implementation]
 
() - ios [implementation]
 
() - ios [implementation]
 
() - ios [implementation]
 

Detailed Description

Definition at line 104 of file FlutterEmbedderKeyResponderTest.mm.

Method Documentation

◆ ios [1/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

123 {
124 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
125 __block BOOL last_handled = TRUE;
127
129 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
130 void* _Nullable user_data) {
131 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
132 callback:callback
133 userData:user_data]];
134 }];
135
136 last_handled = FALSE;
137 [responder handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "a", "a")
138 callback:^(BOOL handled) {
139 last_handled = handled;
140 }];
141
142 XCTAssertEqual([events count], 1u);
143 event = [events lastObject].data;
144 XCTAssertNotEqual(event, nullptr);
145 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
146 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
147 XCTAssertEqual(event->timestamp, 123000000.0f);
148 XCTAssertEqual(event->physical, kPhysicalKeyA);
149 XCTAssertEqual(event->logical, kLogicalKeyA);
150 XCTAssertStrEqual(event->character, "a");
151 XCTAssertEqual(event->synthesized, false);
152
153 XCTAssertEqual(last_handled, FALSE);
154 XCTAssert([[events lastObject] hasCallback]);
155 [[events lastObject] respond:TRUE];
156 XCTAssertEqual(last_handled, TRUE);
157
158 [events removeAllObjects];
159
160 last_handled = TRUE;
161 [responder handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f)
162 callback:^(BOOL handled) {
163 last_handled = handled;
164 }];
165
166 XCTAssertEqual([events count], 1u);
167 event = [events lastObject].data;
168 XCTAssertNotEqual(event, nullptr);
169 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
170 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
171 XCTAssertEqual(event->timestamp, 123000000.0f);
172 XCTAssertEqual(event->physical, kPhysicalKeyA);
173 XCTAssertEqual(event->logical, kLogicalKeyA);
174 XCTAssertEqual(event->character, nullptr);
175 XCTAssertEqual(event->synthesized, false);
176
177 XCTAssertEqual(last_handled, TRUE);
178 XCTAssert([[events lastObject] hasCallback]);
179 [[events lastObject] respond:FALSE]; // Check if responding FALSE works
180 XCTAssertEqual(last_handled, FALSE);
181
182 [events removeAllObjects];
183}
int count
@ kFlutterKeyEventDeviceTypeKeyboard
Definition embedder.h:1079
@ kFlutterKeyEventTypeDown
Definition embedder.h:1074
@ kFlutterKeyEventTypeUp
Definition embedder.h:1073
FlKeyEvent * event
void handlePress:callback:(nonnull FlutterUIPressProxy *press, [callback] ios(13.4) API_AVAILABLE)
#define XCTAssertStrEqual(value, expected)
return FALSE
constexpr uint64_t kPhysicalKeyA
Definition key_codes.g.h:77
constexpr uint64_t kLogicalKeyA
int BOOL

◆ ios [2/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

185 {
186 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
187 __block BOOL last_handled = TRUE;
189
191 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
192 void* _Nullable user_data) {
193 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
194 callback:callback
195 userData:user_data]];
196 }];
197
198 last_handled = FALSE;
199 // Verify that the eject key (keycode 0xb8, which is not present in the keymap)
200 // should be translated to the right logical and physical keys.
201 [responder handlePress:keyDownEvent(kKeyCodeEject, kModifierFlagNone, 123.0f)
202 callback:^(BOOL handled) {
203 last_handled = handled;
204 }];
205
206 XCTAssertEqual([events count], 1u);
207 event = [events lastObject].data;
208 XCTAssertNotEqual(event, nullptr);
209 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
210 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
211 XCTAssertEqual(event->physical, kKeyCodeEject | kIosPlane);
212 XCTAssertEqual(event->logical, kKeyCodeEject | kIosPlane);
213 XCTAssertEqual(event->character, nullptr);
214 XCTAssertEqual(event->synthesized, false);
215
216 XCTAssertEqual(last_handled, FALSE);
217 XCTAssert([[events lastObject] hasCallback]);
218 [[events lastObject] respond:TRUE];
219 XCTAssertEqual(last_handled, TRUE);
220
221 [events removeAllObjects];
222
223 last_handled = TRUE;
224 [responder handlePress:keyUpEvent(kKeyCodeEject, kModifierFlagNone, 123.0f)
225 callback:^(BOOL handled) {
226 last_handled = handled;
227 }];
228
229 XCTAssertEqual([events count], 1u);
230 event = [events lastObject].data;
231 XCTAssertNotEqual(event, nullptr);
232 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
233 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
234 XCTAssertEqual(event->physical, kKeyCodeEject | kIosPlane);
235 XCTAssertEqual(event->logical, kKeyCodeEject | kIosPlane);
236 XCTAssertEqual(event->character, nullptr);
237 XCTAssertEqual(event->synthesized, false);
238
239 XCTAssertEqual(last_handled, TRUE);
240 XCTAssert([[events lastObject] hasCallback]);
241 [[events lastObject] respond:FALSE]; // Check if responding FALSE works
242 XCTAssertEqual(last_handled, FALSE);
243
244 [events removeAllObjects];
245}
const uint64_t kIosPlane

◆ ios [3/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

247 {
248 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
250
252 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
253 void* _Nullable user_data) {
254 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
255 callback:callback
256 userData:user_data]];
257 }];
258
259 // This tests that we synthesize the correct modifier keys when we release the
260 // modifier key that created the letter before we release the letter.
261 [responder handlePress:keyDownEvent(kKeyCodeAltRight, kModifierFlagAltAny, 123.0f)
262 callback:^(BOOL handled){
263 }];
264
265 XCTAssertEqual([events count], 1u);
266 event = [events lastObject].data;
267 XCTAssertNotEqual(event, nullptr);
268 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
269 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
270 XCTAssertEqual(event->physical, kPhysicalAltRight);
271 XCTAssertEqual(event->logical, kLogicalAltRight);
272 XCTAssertEqual(event->character, nullptr);
273 XCTAssertEqual(event->synthesized, false);
274
275 [events removeAllObjects];
276
277 // Test non-ASCII characters being produced.
278 [responder handlePress:keyDownEvent(kKeyCodeKeyW, kModifierFlagAltAny, 123.0f, "∑", "w")
279 callback:^(BOOL handled){
280 }];
281
282 XCTAssertEqual([events count], 1u);
283 event = [events lastObject].data;
284 XCTAssertNotEqual(event, nullptr);
285 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
286 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
287 XCTAssertEqual(event->physical, kPhysicalKeyW);
288 XCTAssertEqual(event->logical, kLogicalKeyW);
289 XCTAssertStrEqual(event->character, "∑");
290 XCTAssertEqual(event->synthesized, false);
291
292 [events removeAllObjects];
293
294 // Releasing the modifier key before the letter should send the key up to the
295 // framework.
296 [responder handlePress:keyUpEvent(kKeyCodeAltRight, kModifierFlagAltAny, 123.0f)
297 callback:^(BOOL handled){
298 }];
299
300 XCTAssertEqual([events count], 1u);
301 event = [events lastObject].data;
302 XCTAssertNotEqual(event, nullptr);
303 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
304 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
305 XCTAssertEqual(event->physical, kPhysicalAltRight);
306 XCTAssertEqual(event->logical, kLogicalAltRight);
307 XCTAssertEqual(event->character, nullptr);
308 XCTAssertEqual(event->synthesized, false);
309
310 [events removeAllObjects];
311
312 // Yes, iOS sends a modifier flag for the Alt key being down on this event,
313 // even though the Alt (Option) key has already been released. This means that
314 // for the framework to be in the correct state, we must synthesize a key down
315 // event for the modifier key here, and another key up before the next key
316 // event.
317 [responder handlePress:keyUpEvent(kKeyCodeKeyW, kModifierFlagAltAny, 123.0f)
318 callback:^(BOOL handled){
319 }];
320
321 XCTAssertEqual([events count], 1u);
322 event = [events lastObject].data;
323 XCTAssertNotEqual(event, nullptr);
324 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
325 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
326 XCTAssertEqual(event->physical, kPhysicalKeyW);
327 XCTAssertEqual(event->logical, kLogicalKeyW);
328 XCTAssertEqual(event->character, nullptr);
329 XCTAssertEqual(event->synthesized, false);
330
331 [events removeAllObjects];
332
333 // Here we should simulate a key up for the Alt key, since it is no longer
334 // shown as down in the modifier flags.
335 [responder handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "å", "a")
336 callback:^(BOOL handled){
337 }];
338
339 XCTAssertEqual([events count], 1u);
340 event = [events lastObject].data;
341 XCTAssertNotEqual(event, nullptr);
342 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
343 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
344 XCTAssertEqual(event->physical, kPhysicalKeyA);
345 XCTAssertEqual(event->logical, kLogicalKeyA);
346 XCTAssertStrEqual(event->character, "å");
347 XCTAssertEqual(event->synthesized, false);
348}
constexpr uint64_t kPhysicalAltRight
constexpr uint64_t kPhysicalKeyW
Definition key_codes.g.h:99
constexpr uint64_t kLogicalKeyW
constexpr uint64_t kLogicalAltRight

◆ ios [4/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

350 {
351 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
352 __block BOOL last_handled = TRUE;
354
356 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
357 void* _Nullable user_data) {
358 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
359 callback:callback
360 userData:user_data]];
361 }];
362
363 last_handled = FALSE;
364 [responder handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "a", "a")
365 callback:^(BOOL handled) {
366 last_handled = handled;
367 }];
368
369 XCTAssertEqual([events count], 1u);
370 event = [events lastObject].data;
371 XCTAssertNotEqual(event, nullptr);
372 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
373 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
374 XCTAssertEqual(event->physical, kPhysicalKeyA);
375 XCTAssertEqual(event->logical, kLogicalKeyA);
376 XCTAssertStrEqual(event->character, "a");
377 XCTAssertEqual(event->synthesized, false);
378 XCTAssertEqual(last_handled, FALSE);
379 [[events lastObject] respond:TRUE];
380 XCTAssertEqual(last_handled, TRUE);
381
382 [events removeAllObjects];
383
384 last_handled = FALSE;
385 [responder handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "a", "a")
386 callback:^(BOOL handled) {
387 last_handled = handled;
388 }];
389
390 XCTAssertEqual([events count], 1u);
391 event = [events lastObject].data;
392 XCTAssertNotEqual(event, nullptr);
393 XCTAssertEqual(event->physical, 0ull);
394 XCTAssertEqual(event->logical, 0ull);
395 XCTAssertEqual(event->synthesized, false);
396 XCTAssertFalse([[events lastObject] hasCallback]);
397 XCTAssertEqual(last_handled, TRUE);
398
399 [events removeAllObjects];
400
401 last_handled = FALSE;
402 [responder handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f)
403 callback:^(BOOL handled) {
404 last_handled = handled;
405 }];
406
407 XCTAssertEqual([events count], 1u);
408 event = [events lastObject].data;
409 XCTAssertNotEqual(event, nullptr);
410 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
411 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
412 XCTAssertEqual(event->physical, kPhysicalKeyA);
413 XCTAssertEqual(event->logical, kLogicalKeyA);
414 XCTAssertEqual(event->character, nullptr);
415 XCTAssertEqual(event->synthesized, false);
416 XCTAssertEqual(last_handled, FALSE);
417 [[events lastObject] respond:TRUE];
418 XCTAssertEqual(last_handled, TRUE);
419
420 [events removeAllObjects];
421}

◆ ios [5/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

423 {
424 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
425 __block BOOL last_handled = TRUE;
427
429 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
430 void* _Nullable user_data) {
431 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
432 callback:callback
433 userData:user_data]];
434 }];
435
436 last_handled = FALSE;
437 [responder handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f)
438 callback:^(BOOL handled) {
439 last_handled = handled;
440 }];
441
442 XCTAssertEqual([events count], 1u);
443 event = [events lastObject].data;
444 XCTAssertNotEqual(event, nullptr);
445 XCTAssertEqual(event->physical, 0ull);
446 XCTAssertEqual(event->logical, 0ull);
447 XCTAssertEqual(event->synthesized, false);
448 XCTAssertFalse([[events lastObject] hasCallback]);
449 XCTAssertEqual(last_handled, TRUE);
450
451 [events removeAllObjects];
452}

◆ ios [6/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

458 {
459 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
461
463 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
464 void* _Nullable user_data) {
465 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
466 callback:callback
467 userData:user_data]];
468 }];
469
470 [responder handlePress:keyDownEvent(kKeyCodeShiftRight, kModifierFlagShiftAny, 123.0f)
471 callback:^(BOOL handled){
472 }];
473
474 XCTAssertEqual([events count], 1u);
475
476 event = [events lastObject].data;
477 XCTAssertNotEqual(event, nullptr);
478 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
479 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
480 XCTAssertEqual(event->timestamp, 123000000.0f);
481 XCTAssertEqual(event->physical, kPhysicalShiftRight);
482 XCTAssertEqual(event->logical, kLogicalShiftRight);
483 XCTAssertEqual(event->character, nullptr);
484 XCTAssertEqual(event->synthesized, false);
485 [[events lastObject] respond:TRUE];
486
487 [events removeAllObjects];
488
489 [responder handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagShiftAny, 123.0f, "A", "A")
490 callback:^(BOOL handled){
491 }];
492
493 XCTAssertEqual([events count], 1u);
494 event = [events lastObject].data;
495 XCTAssertNotEqual(event, nullptr);
496 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
497 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
498 XCTAssertEqual(event->physical, kPhysicalKeyA);
499 XCTAssertEqual(event->logical, kLogicalKeyA);
500 XCTAssertStrEqual(event->character, "A");
501 XCTAssertEqual(event->synthesized, false);
502 [[events lastObject] respond:TRUE];
503
504 [events removeAllObjects];
505
506 [responder handlePress:keyUpEvent(kKeyCodeShiftRight, kModifierFlagNone, 123.0f)
507 callback:^(BOOL handled){
508 }];
509
510 XCTAssertEqual([events count], 1u);
511 event = [events lastObject].data;
512 XCTAssertNotEqual(event, nullptr);
513 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
514 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
515 XCTAssertEqual(event->physical, kPhysicalShiftRight);
516 XCTAssertEqual(event->logical, kLogicalShiftRight);
517 XCTAssertEqual(event->character, nullptr);
518 XCTAssertEqual(event->synthesized, false);
519 [[events lastObject] respond:TRUE];
520
521 [events removeAllObjects];
522
523 [responder handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f)
524 callback:^(BOOL handled){
525 }];
526
527 XCTAssertEqual([events count], 1u);
528 event = [events lastObject].data;
529 XCTAssertNotEqual(event, nullptr);
530 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
531 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
532 XCTAssertEqual(event->physical, kPhysicalKeyA);
533 XCTAssertEqual(event->logical, kLogicalKeyA);
534 XCTAssertEqual(event->character, nullptr);
535 XCTAssertEqual(event->synthesized, false);
536 [[events lastObject] respond:TRUE];
537
538 [events removeAllObjects];
539}
constexpr uint64_t kLogicalShiftRight
constexpr uint64_t kPhysicalShiftRight

◆ ios [7/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

546 {
547 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
549 __block BOOL last_handled = TRUE;
550 id keyEventCallback = ^(BOOL handled) {
551 last_handled = handled;
552 };
553
555 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
556 void* _Nullable user_data) {
557 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
558 callback:callback
559 userData:user_data]];
560 }];
561
562 // Keydown: Numpad1, Fn (undefined), F1, KeyA, ShiftLeft
563 // Then KeyUp: Numpad1, Fn (undefined), F1, KeyA, ShiftLeft
564
565 // Numpad 1
566 // OS provides: char: "1", code: 0x59, modifiers: 0x200000
567 [responder handlePress:keyDownEvent(kKeyCodeNumpad1, kModifierFlagNumPadKey, 123.0, "1", "1")
568 callback:keyEventCallback];
569
570 XCTAssertEqual([events count], 1u);
571 event = [events lastObject].data;
572 XCTAssertNotEqual(event, nullptr);
573 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
574 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
575 XCTAssertEqual(event->physical, kPhysicalNumpad1);
576 XCTAssertEqual(event->logical, kLogicalNumpad1);
577 XCTAssertStrEqual(event->character, "1");
578 XCTAssertEqual(event->synthesized, false);
579 [[events lastObject] respond:TRUE];
580
581 [events removeAllObjects];
582
583 // Fn Key (sends HID undefined)
584 // OS provides: char: nil, keycode: 0x3, modifiers: 0x0
585 [responder handlePress:keyDownEvent(kKeyCodeUndefined, kModifierFlagNone, 123.0)
586 callback:keyEventCallback];
587
588 XCTAssertEqual([events count], 1u);
589 event = [events lastObject].data;
590 XCTAssertNotEqual(event, nullptr);
591 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
592 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
593 XCTAssertEqual(event->physical, kPhysicalKeyUndefined);
594 XCTAssertEqual(event->logical, kLogicalKeyUndefined);
595 XCTAssertEqual(event->character, nullptr);
596 XCTAssertEqual(event->synthesized, false);
597 [[events lastObject] respond:TRUE];
598
599 [events removeAllObjects];
600
601 // F1 Down
602 // OS provides: char: UIKeyInputF1, code: 0x3a, modifiers: 0x0
603 [responder handlePress:keyDownEvent(kKeyCodeF1, kModifierFlagNone, 123.0f, "\\^P", "\\^P")
604 callback:keyEventCallback];
605
606 XCTAssertEqual([events count], 1u);
607 event = [events lastObject].data;
608 XCTAssertNotEqual(event, nullptr);
609 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
610 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
611 XCTAssertEqual(event->physical, kPhysicalF1);
612 XCTAssertEqual(event->logical, kLogicalF1);
613 XCTAssertEqual(event->character, nullptr);
614 XCTAssertEqual(event->synthesized, false);
615 [[events lastObject] respond:TRUE];
616
617 [events removeAllObjects];
618
619 // KeyA Down
620 // OS provides: char: "q", code: 0x4, modifiers: 0x0
621 [responder handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "a", "a")
622 callback:keyEventCallback];
623
624 XCTAssertEqual([events count], 1u);
625 event = [events lastObject].data;
626 XCTAssertNotEqual(event, nullptr);
627 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
628 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
629 XCTAssertEqual(event->physical, kPhysicalKeyA);
630 XCTAssertEqual(event->logical, kLogicalKeyA);
631 XCTAssertStrEqual(event->character, "a");
632 XCTAssertEqual(event->synthesized, false);
633 [[events lastObject] respond:TRUE];
634
635 [events removeAllObjects];
636
637 // ShiftLeft Down
638 // OS Provides: char: nil, code: 0xe1, modifiers: 0x20000
639 [responder handlePress:keyDownEvent(kKeyCodeShiftLeft, kModifierFlagShiftAny, 123.0f)
640 callback:keyEventCallback];
641
642 XCTAssertEqual([events count], 1u);
643 event = [events lastObject].data;
644 XCTAssertNotEqual(event, nullptr);
645 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
646 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
647 XCTAssertEqual(event->physical, kPhysicalShiftLeft);
648 XCTAssertEqual(event->logical, kLogicalShiftLeft);
649 XCTAssertEqual(event->character, nullptr);
650 XCTAssertEqual(event->synthesized, false);
651
652 [events removeAllObjects];
653
654 // Numpad 1 Up
655 // OS provides: char: "1", code: 0x59, modifiers: 0x200000
656 [responder handlePress:keyUpEvent(kKeyCodeNumpad1, kModifierFlagNumPadKey, 123.0f)
657 callback:keyEventCallback];
658
659 XCTAssertEqual([events count], 2u);
660
661 // Because the OS no longer provides the 0x20000 (kModifierFlagShiftAny), we
662 // have to simulate a keyup.
663 event = [events firstObject].data;
664 XCTAssertNotEqual(event, nullptr);
665 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
666 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
667 XCTAssertEqual(event->physical, kPhysicalShiftLeft);
668 XCTAssertEqual(event->logical, kLogicalShiftLeft);
669 XCTAssertEqual(event->character, nullptr);
670 XCTAssertEqual(event->synthesized, true);
671
672 event = [events lastObject].data;
673 XCTAssertNotEqual(event, nullptr);
674 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
675 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
676 XCTAssertEqual(event->physical, kPhysicalNumpad1);
677 XCTAssertEqual(event->logical, kLogicalNumpad1);
678 XCTAssertEqual(event->character, nullptr);
679 XCTAssertEqual(event->synthesized, false);
680 [[events lastObject] respond:TRUE];
681
682 [events removeAllObjects];
683
684 // F1 Up
685 // OS provides: char: UIKeyInputF1, code: 0x3a, modifiers: 0x0
686 [responder handlePress:keyUpEvent(kKeyCodeF1, kModifierFlagNone, 123.0f)
687 callback:keyEventCallback];
688
689 XCTAssertEqual([events count], 1u);
690 event = [events lastObject].data;
691 XCTAssertNotEqual(event, nullptr);
692 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
693 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
694 XCTAssertEqual(event->physical, kPhysicalF1);
695 XCTAssertEqual(event->logical, kLogicalF1);
696 XCTAssertEqual(event->character, nullptr);
697 XCTAssertEqual(event->synthesized, false);
698 [[events lastObject] respond:TRUE];
699
700 [events removeAllObjects];
701
702 // Fn Key (sends HID undefined)
703 // OS provides: char: nil, code: 0x3, modifiers: 0x0
704 [responder handlePress:keyUpEvent(kKeyCodeUndefined, kModifierFlagNone, 123.0)
705 callback:keyEventCallback];
706
707 XCTAssertEqual([events count], 1u);
708 event = [events lastObject].data;
709 XCTAssertNotEqual(event, nullptr);
710 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
711 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
712 XCTAssertEqual(event->physical, kPhysicalKeyUndefined);
713 XCTAssertEqual(event->logical, kLogicalKeyUndefined);
714 XCTAssertEqual(event->synthesized, false);
715 [[events lastObject] respond:TRUE];
716
717 [events removeAllObjects];
718
719 // KeyA Up
720 // OS provides: char: "a", code: 0x4, modifiers: 0x0
721 [responder handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f)
722 callback:keyEventCallback];
723
724 XCTAssertEqual([events count], 1u);
725 event = [events lastObject].data;
726 XCTAssertNotEqual(event, nullptr);
727 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
728 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
729 XCTAssertEqual(event->physical, kPhysicalKeyA);
730 XCTAssertEqual(event->logical, kLogicalKeyA);
731 XCTAssertEqual(event->character, nullptr);
732 XCTAssertEqual(event->synthesized, false);
733 [[events lastObject] respond:TRUE];
734
735 [events removeAllObjects];
736
737 // ShiftLeft Up
738 // OS provides: char: nil, code: 0xe1, modifiers: 0x20000
739 [responder handlePress:keyUpEvent(kKeyCodeShiftLeft, kModifierFlagShiftAny, 123.0f)
740 callback:keyEventCallback];
741
742 XCTAssertEqual([events count], 1u);
743 event = [events lastObject].data;
744 XCTAssertNotEqual(event, nullptr);
745 XCTAssertEqual(event->physical, 0ull);
746 XCTAssertEqual(event->logical, 0ull);
747 XCTAssertEqual(event->synthesized, false);
748 XCTAssertFalse([[events lastObject] hasCallback]);
749 XCTAssertEqual(last_handled, TRUE);
750
751 [events removeAllObjects];
752}
constexpr uint64_t kLogicalF1
constexpr uint64_t kLogicalNumpad1
constexpr uint64_t kPhysicalShiftLeft
constexpr uint64_t kPhysicalF1
constexpr uint64_t kPhysicalNumpad1
constexpr uint64_t kLogicalShiftLeft

◆ ios [8/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

754 {
755 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
757
759 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
760 void* _Nullable user_data) {
761 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
762 callback:callback
763 userData:user_data]];
764 }];
765
766 [responder handlePress:keyDownEvent(kKeyCodeShiftLeft, kModifierFlagShiftAny, 123.0f)
767 callback:^(BOOL handled){
768 }];
769
770 XCTAssertEqual([events count], 1u);
771 event = [events lastObject].data;
772 XCTAssertNotEqual(event, nullptr);
773 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
774 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
775 XCTAssertEqual(event->physical, kPhysicalShiftLeft);
776 XCTAssertEqual(event->logical, kLogicalShiftLeft);
777 XCTAssertEqual(event->character, nullptr);
778 XCTAssertEqual(event->synthesized, false);
779 [[events lastObject] respond:TRUE];
780
781 [events removeAllObjects];
782
783 [responder handlePress:keyDownEvent(kKeyCodeShiftRight, kModifierFlagShiftAny, 123.0f)
784 callback:^(BOOL handled){
785 }];
786
787 XCTAssertEqual([events count], 1u);
788 event = [events lastObject].data;
789 XCTAssertNotEqual(event, nullptr);
790 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
791 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
792 XCTAssertEqual(event->physical, kPhysicalShiftRight);
793 XCTAssertEqual(event->logical, kLogicalShiftRight);
794 XCTAssertEqual(event->character, nullptr);
795 XCTAssertEqual(event->synthesized, false);
796 [[events lastObject] respond:TRUE];
797
798 [events removeAllObjects];
799
800 [responder handlePress:keyUpEvent(kKeyCodeShiftLeft, kModifierFlagShiftAny, 123.0f)
801 callback:^(BOOL handled){
802 }];
803
804 XCTAssertEqual([events count], 1u);
805 event = [events lastObject].data;
806 XCTAssertNotEqual(event, nullptr);
807 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
808 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
809 XCTAssertEqual(event->physical, kPhysicalShiftLeft);
810 XCTAssertEqual(event->logical, kLogicalShiftLeft);
811 XCTAssertEqual(event->character, nullptr);
812 XCTAssertEqual(event->synthesized, false);
813 [[events lastObject] respond:TRUE];
814
815 [events removeAllObjects];
816
817 [responder handlePress:keyUpEvent(kKeyCodeShiftRight, kModifierFlagShiftAny, 123.0f)
818 callback:^(BOOL handled){
819 }];
820
821 XCTAssertEqual([events count], 1u);
822 event = [events lastObject].data;
823 XCTAssertNotEqual(event, nullptr);
824 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
825 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
826 XCTAssertEqual(event->physical, kPhysicalShiftRight);
827 XCTAssertEqual(event->logical, kLogicalShiftRight);
828 XCTAssertEqual(event->character, nullptr);
829 XCTAssertEqual(event->synthesized, false);
830 [[events lastObject] respond:TRUE];
831
832 [events removeAllObjects];
833}

◆ ios [9/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

836 {
837 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
838 __block BOOL last_handled = TRUE;
839 id keyEventCallback = ^(BOOL handled) {
840 last_handled = handled;
841 };
843
845 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
846 void* _Nullable user_data) {
847 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
848 callback:callback
849 userData:user_data]];
850 }];
851
852 last_handled = FALSE;
853 [responder handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagCapsLock, 123.0f, "A", "A")
854 callback:keyEventCallback];
855
856 XCTAssertEqual([events count], 3u);
857
858 event = events[0].data;
859 XCTAssertNotEqual(event, nullptr);
860 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
861 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
862 XCTAssertEqual(event->physical, kPhysicalCapsLock);
863 XCTAssertEqual(event->logical, kLogicalCapsLock);
864 XCTAssertEqual(event->character, nullptr);
865 XCTAssertEqual(event->synthesized, true);
866 XCTAssertFalse([events[0] hasCallback]);
867
868 event = events[1].data;
869 XCTAssertNotEqual(event, nullptr);
870 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
871 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
872 XCTAssertEqual(event->physical, kPhysicalCapsLock);
873 XCTAssertEqual(event->logical, kLogicalCapsLock);
874 XCTAssertEqual(event->character, nullptr);
875 XCTAssertEqual(event->synthesized, true);
876 XCTAssertFalse([events[1] hasCallback]);
877
878 event = events[2].data;
879 XCTAssertNotEqual(event, nullptr);
880 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
881 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
882 XCTAssertEqual(event->physical, kPhysicalKeyA);
883 XCTAssertEqual(event->logical, kLogicalKeyA);
884 XCTAssertStrEqual(event->character, "A");
885 XCTAssertEqual(event->synthesized, false);
886 XCTAssert([events[2] hasCallback]);
887
888 XCTAssertEqual(last_handled, FALSE);
889 [[events lastObject] respond:TRUE];
890 XCTAssertEqual(last_handled, TRUE);
891
892 [events removeAllObjects];
893
894 // Release the "A" key.
895 [responder handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagCapsLock, 123.0f)
896 callback:keyEventCallback];
897 XCTAssertEqual([events count], 1u);
898 event = [events lastObject].data;
899 XCTAssertNotEqual(event, nullptr);
900 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
901 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
902 XCTAssertEqual(event->physical, kPhysicalKeyA);
903 XCTAssertEqual(event->logical, kLogicalKeyA);
904 XCTAssertEqual(event->synthesized, false);
905
906 [events removeAllObjects];
907
908 // In: CapsLock down
909 // Out: CapsLock down
910 last_handled = FALSE;
911 [responder handlePress:keyDownEvent(kKeyCodeCapsLock, kModifierFlagCapsLock, 123.0f)
912 callback:keyEventCallback];
913
914 XCTAssertEqual([events count], 1u);
915 event = [events firstObject].data;
916 XCTAssertNotEqual(event, nullptr);
917 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
918 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
919 XCTAssertEqual(event->physical, kPhysicalCapsLock);
920 XCTAssertEqual(event->logical, kLogicalCapsLock);
921 XCTAssertEqual(event->character, nullptr);
922 XCTAssertEqual(event->synthesized, false);
923 XCTAssert([[events firstObject] hasCallback]);
924
925 [events removeAllObjects];
926
927 // In: CapsLock up
928 // Out: CapsLock up
929 // This turns off the caps lock, triggering a synthesized up/down to tell the
930 // framework that.
931 last_handled = FALSE;
932 [responder handlePress:keyUpEvent(kKeyCodeCapsLock, kModifierFlagCapsLock, 123.0f)
933 callback:keyEventCallback];
934
935 XCTAssertEqual([events count], 1u);
936 event = [events firstObject].data;
937 XCTAssertNotEqual(event, nullptr);
938 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
939 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
940 XCTAssertEqual(event->physical, kPhysicalCapsLock);
941 XCTAssertEqual(event->logical, kLogicalCapsLock);
942 XCTAssertEqual(event->character, nullptr);
943 XCTAssertEqual(event->synthesized, false);
944 XCTAssert([[events firstObject] hasCallback]);
945
946 [events removeAllObjects];
947
948 last_handled = FALSE;
949 [responder handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "a", "a")
950 callback:keyEventCallback];
951
952 // Just to make sure that we aren't simulating events now, since the state is
953 // consistent, and should be off.
954 XCTAssertEqual([events count], 1u);
955 event = [events lastObject].data;
956 XCTAssertNotEqual(event, nullptr);
957 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
958 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
959 XCTAssertEqual(event->physical, kPhysicalKeyA);
960 XCTAssertEqual(event->logical, kLogicalKeyA);
961 XCTAssertStrEqual(event->character, "a");
962 XCTAssertEqual(event->synthesized, false);
963 XCTAssert([[events firstObject] hasCallback]);
964}
constexpr uint64_t kPhysicalCapsLock
constexpr uint64_t kLogicalCapsLock

◆ ios [10/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

967 {
968 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
969 __block BOOL last_handled = TRUE;
970 id keyEventCallback = ^(BOOL handled) {
971 last_handled = handled;
972 };
974
976 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
977 void* _Nullable user_data) {
978 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
979 callback:callback
980 userData:user_data]];
981 }];
982
983 last_handled = FALSE;
984 [responder handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagCapsLock, 123.0f, "A", "a")
985 callback:keyEventCallback];
986
987 XCTAssertEqual([events count], 3u);
988
989 event = events[0].data;
990 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
991 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
992 XCTAssertEqual(event->physical, kPhysicalCapsLock);
993 XCTAssertEqual(event->logical, kLogicalCapsLock);
994 XCTAssertEqual(event->character, nullptr);
995 XCTAssertEqual(event->synthesized, true);
996 XCTAssertFalse([events[0] hasCallback]);
997
998 event = events[1].data;
999 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
1000 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1001 XCTAssertEqual(event->physical, kPhysicalCapsLock);
1002 XCTAssertEqual(event->logical, kLogicalCapsLock);
1003 XCTAssertEqual(event->character, nullptr);
1004 XCTAssertEqual(event->synthesized, true);
1005 XCTAssertFalse([events[1] hasCallback]);
1006
1007 event = events[2].data;
1008 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
1009 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1010 XCTAssertEqual(event->physical, kPhysicalKeyA);
1011 XCTAssertEqual(event->logical, kLogicalKeyA);
1012 XCTAssertStrEqual(event->character, "A");
1013 XCTAssertEqual(event->synthesized, false);
1014 XCTAssert([events[2] hasCallback]);
1015
1016 XCTAssertEqual(last_handled, FALSE);
1017 [[events lastObject] respond:TRUE];
1018 XCTAssertEqual(last_handled, TRUE);
1019
1020 [events removeAllObjects];
1021}

◆ ios [11/11]

- ios
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

1024 {
1025 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
1026 id keyEventCallback = ^(BOOL handled) {
1027 };
1029
1031 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
1032 void* _Nullable user_data) {
1033 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event callback:nil userData:nil]];
1034 callback(true, user_data);
1035 }];
1036
1037 // MetaLeft down.
1038 [responder handlePress:keyDownEvent(kKeyCodeCommandLeft, kModifierFlagMetaAny, 123.0f, "", "")
1039 callback:keyEventCallback];
1040 XCTAssertEqual([events count], 1u);
1041 event = events[0].data;
1042 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
1043 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1044 XCTAssertEqual(event->physical, kPhysicalMetaLeft);
1045 XCTAssertEqual(event->logical, kLogicalMetaLeft);
1046 XCTAssertEqual(event->character, nullptr);
1047 XCTAssertEqual(event->synthesized, false);
1048 [events removeAllObjects];
1049
1050 // Period down, which is logically Escape.
1051 [responder handlePress:keyDownEvent(kKeyCodePeriod, kModifierFlagMetaAny, 123.0f,
1052 "UIKeyInputEscape", "UIKeyInputEscape")
1053 callback:keyEventCallback];
1054 XCTAssertEqual([events count], 1u);
1055 event = events[0].data;
1056 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
1057 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1058 XCTAssertEqual(event->physical, kPhysicalPeriod);
1059 XCTAssertEqual(event->logical, kLogicalEscape);
1060 XCTAssertEqual(event->character, nullptr);
1061 XCTAssertEqual(event->synthesized, false);
1062 [events removeAllObjects];
1063
1064 // Period up, which unconventionally has characters.
1065 [responder handlePress:keyUpEvent(kKeyCodePeriod, kModifierFlagMetaAny, 123.0f,
1066 "UIKeyInputEscape", "UIKeyInputEscape")
1067 callback:keyEventCallback];
1068 XCTAssertEqual([events count], 1u);
1069 event = events[0].data;
1070 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
1071 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1072 XCTAssertEqual(event->physical, kPhysicalPeriod);
1073 XCTAssertEqual(event->logical, kLogicalEscape);
1074 XCTAssertEqual(event->character, nullptr);
1075 XCTAssertEqual(event->synthesized, false);
1076 [events removeAllObjects];
1077
1078 // MetaLeft up.
1079 [responder handlePress:keyUpEvent(kKeyCodeCommandLeft, kModifierFlagMetaAny, 123.0f, "", "")
1080 callback:keyEventCallback];
1081 XCTAssertEqual([events count], 1u);
1082 event = events[0].data;
1083 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
1084 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1085 XCTAssertEqual(event->physical, kPhysicalMetaLeft);
1086 XCTAssertEqual(event->logical, kLogicalMetaLeft);
1087 XCTAssertEqual(event->character, nullptr);
1088 XCTAssertEqual(event->synthesized, false);
1089 [events removeAllObjects];
1090}
constexpr uint64_t kLogicalEscape
constexpr uint64_t kLogicalMetaLeft
constexpr uint64_t kPhysicalMetaLeft
constexpr uint64_t kPhysicalPeriod

◆ setUp

- (void) setUp
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

109 {
110 // All of these tests were designed to run on iOS 13.4 or later.
111 if (@available(iOS 13.4, *)) {
112 } else {
113 XCTSkip(@"Required API not present for test.");
114 }
115}

◆ tearDown

- (void) tearDown
implementation

Definition at line 101 of file FlutterEmbedderKeyResponderTest.mm.

117 {
118}

The documentation for this class was generated from the following file: