summaryrefslogtreecommitdiff
path: root/tests/cocoa/test_keyboard.m
blob: f93a7454e8125ba81d0a7acd0daf3a5e3bef1deb (plain)
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
#import <AppKit/AppKit.h>
#include <yasl/native.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_event next(struct yasl_surface *surface, enum yasl_event_type type)
{
    struct yasl_event event;
    assert(yasl_surface_poll_event(surface, &event));
    assert(event.type == type && event.surface == surface);
    return event;
}

static void empty(struct yasl_surface *surface)
{
    struct yasl_event event;
    assert(!yasl_surface_poll_event(surface, &event));
}

int main(void)
{
    @autoreleasepool {
        struct yasl_display *display = yasl_display_open(NULL, NULL);
        assert(display);
        struct yasl_window_spec spec = {.title = "Keyboard test", .size = {320, 240}};
        struct yasl_surface *surface = yasl_surface_create_toplevel(display, &spec);
        assert(surface);
        NSWindow *window = (NSWindow *)yasl_surface_get_native_handle(surface);
        NSView<NSTextInputClient> *view = (id)window.contentView;
        struct yasl_event event;
        while (yasl_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_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_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_EVENT_KEY);
        assert(event.key.key == YASL_KEY_LEFT_SHIFT && event.key.action == YASL_KEY_PRESS);
        assert(event.key.mods == YASL_MOD_SHIFT && !event.key.codepoint && !event.key.shifted);
        sample.modifierFlags = 0;
        [view flagsChanged:native];
        assert(next(surface, YASL_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(NSNotFound, 0)];
        assert(strcmp(yasl_surface_ime_preedit(surface), "é") == 0);
        empty(surface);
        [view insertText:@"😀é" replacementRange:NSMakeRange(NSNotFound, 0)];
        event = next(surface, YASL_EVENT_TEXT);
        assert(event.text.len == strlen("😀é") && strcmp(event.text.data, "😀é") == 0);
        assert(!yasl_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_EVENT_KEY).key.key == YASL_KEY_A);
        event = next(surface, YASL_EVENT_TEXT);
        assert(event.text.len == 1 && event.text.data[0] == 'a');
        empty(surface);

        sample.type = NSEventTypeScrollWheel;
        sample.hasPreciseScrollingDeltas = YES;
        sample.scrollingDeltaX = -2.5;
        sample.scrollingDeltaY = 1.25;
        [view scrollWheel:native];
        event = next(surface, YASL_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_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_surface_destroy(surface);
        yasl_display_close(display);
    }
    return 0;
}