1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
#import <AppKit/AppKit.h>
#include <common/private_native.h>
#include <common/backend.h>
#include <assert.h>
#include <math.h>
#include <string.h>
/* Reject character queries on modifier-only events, as AppKit does. */
@interface InputSample : NSObject
@property NSEventType type;
@property unsigned short keyCode;
@property NSEventModifierFlags modifierFlags;
@property(copy) NSString *label;
@property(copy) NSString *shiftedLabel;
@property NSPoint locationInWindow;
@property CGFloat scrollingDeltaX;
@property CGFloat scrollingDeltaY;
@property BOOL hasPreciseScrollingDeltas;
@end
@implementation InputSample
- (NSString *)charactersByApplyingModifiers:(NSEventModifierFlags)modifiers
{
assert(self.type == NSEventTypeKeyDown || self.type == NSEventTypeKeyUp);
return modifiers & NSEventModifierFlagShift ? self.shiftedLabel : self.label;
}
- (void)dealloc
{
[_label release];
[_shiftedLabel release];
[super dealloc];
}
@end
static struct yasl_private_event next(struct yasl_surface *surface, enum yasl_private_event_type type)
{
struct yasl_private_event event;
assert(yasl_private_surface_poll_event(surface, &event));
assert(event.type == type && event.surface == surface);
return event;
}
static void empty(struct yasl_surface *surface)
{
struct yasl_private_event event;
assert(!yasl_private_surface_poll_event(surface, &event));
}
int main(void)
{
@autoreleasepool {
struct yasl_private_display *display = yasl_private_display_open(NULL, NULL);
assert(display);
struct yasl_private_window_spec spec = {.title = "Keyboard test", .size = {320, 240}};
struct yasl_surface *surface = yasl_private_surface_create_toplevel(display, &spec);
assert(surface);
surface->text_input_active = true;
surface->text_surrounding = strdup("A😀B");
surface->text_length = strlen(surface->text_surrounding);
surface->text_begin = 1;
surface->text_end = 5;
surface->text_revision = 7;
surface->text_generation = 3;
assert(display->ops->surface_update_text_input(surface));
NSWindow *window = (NSWindow *)yasl_private_surface_get_native_handle(surface);
NSView<NSTextInputClient> *view = (id)window.contentView;
struct yasl_private_event event;
while (yasl_private_surface_poll_event(surface, &event)) {}
InputSample *sample = [InputSample new];
NSEvent *native = (NSEvent *)sample;
sample.type = NSEventTypeKeyUp;
sample.keyCode = 0; /* US A, even with a different active layout. */
sample.label = @"q";
sample.shiftedLabel = @"Q";
[view keyUp:native];
event = next(surface, YASL_PRIVATE_EVENT_KEY);
assert(event.key.key == YASL_KEY_A && event.key.native_code == 0);
assert(event.key.action == YASL_KEY_RELEASE);
assert(event.key.codepoint == 'q' && event.key.shifted == 'Q');
empty(surface);
sample.label = @"😀"; /* One scalar, two UTF-16 units. */
sample.shiftedLabel = @"ab";
[view keyUp:native];
event = next(surface, YASL_PRIVATE_EVENT_KEY);
assert(event.key.codepoint == 0x1f600 && event.key.shifted == 0);
sample.type = NSEventTypeFlagsChanged;
sample.keyCode = 56; /* Left Shift. */
sample.modifierFlags = NSEventModifierFlagShift | 0x0002;
[view flagsChanged:native];
event = next(surface, YASL_PRIVATE_EVENT_KEY);
assert(event.key.key == YASL_KEY_LEFT_SHIFT && event.key.action == YASL_KEY_PRESS);
assert(event.key.mods == YASL_PRIVATE_MOD_SHIFT && !event.key.codepoint && !event.key.shifted);
sample.modifierFlags = 0;
[view flagsChanged:native];
assert(next(surface, YASL_PRIVATE_EVENT_KEY).key.action == YASL_KEY_RELEASE);
/* Native text services own preedit and commits. Marked text is not an
* insertion, and commits retain all UTF-8 bytes, including non-BMP. */
[view setMarkedText:@"é" selectedRange:NSMakeRange(1, 0)
replacementRange:NSMakeRange(1, 2)];
assert(strcmp(yasl_private_surface_ime_preedit(surface), "é") == 0);
empty(surface);
[view insertText:@"😀é" replacementRange:NSMakeRange(NSNotFound, 0)];
event = next(surface, YASL_PRIVATE_EVENT_TEXT_EDIT);
assert(event.text_edit.len == strlen("😀é") && strcmp(event.text_edit.data, "😀é") == 0);
assert(event.text_edit.begin == 1 && event.text_edit.end == 5);
assert(event.text_edit.revision == 7 && event.text_edit.generation == 3);
assert(!yasl_private_surface_ime_preedit(surface));
empty(surface);
/* Exercise the keyDown -> interpretKeyEvents -> insertText route with
* an actual AppKit key event rather than the strict event double. */
NSEvent *press = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSZeroPoint
modifierFlags:0 timestamp:0 windowNumber:window.windowNumber context:nil
characters:@"a" charactersIgnoringModifiers:@"a" isARepeat:NO keyCode:0];
[view keyDown:press];
assert(next(surface, YASL_PRIVATE_EVENT_KEY).key.key == YASL_KEY_A);
event = next(surface, YASL_PRIVATE_EVENT_TEXT_EDIT);
assert(event.text_edit.len == 1 && event.text_edit.data[0] == 'a');
assert(event.text_edit.begin == 1 && event.text_edit.end == 5);
empty(surface);
surface->text_end = 1;
[view doCommandBySelector:@selector(deleteBackward)];
event = next(surface, YASL_PRIVATE_EVENT_TEXT_EDIT);
assert(event.text_edit.len == 0 && event.text_edit.begin == 0 && event.text_edit.end == 1);
empty(surface);
sample.type = NSEventTypeScrollWheel;
sample.hasPreciseScrollingDeltas = YES;
sample.scrollingDeltaX = -2.5;
sample.scrollingDeltaY = 1.25;
[view scrollWheel:native];
event = next(surface, YASL_PRIVATE_EVENT_POINTER_SCROLL);
assert(event.pointer.scroll.x == 0.25 && event.pointer.scroll.y == -0.125);
sample.hasPreciseScrollingDeltas = NO;
sample.scrollingDeltaX = 1;
sample.scrollingDeltaY = -1;
[view scrollWheel:native];
event = next(surface, YASL_PRIVATE_EVENT_POINTER_SCROLL);
assert(event.pointer.scroll.x == -1 && event.pointer.scroll.y == 1);
sample.scrollingDeltaX = NAN;
[view scrollWheel:native];
empty(surface);
[sample release];
yasl_private_surface_destroy(surface);
yasl_private_display_close(display);
}
return 0;
}
|