Coverage Report

Created: 2026-11-11 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/input/input.c
Line
Count
Source
1
/*
2
 * This file is part of mpv.
3
 *
4
 * mpv is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * mpv is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
#include "config.h"
19
20
#include <stdlib.h>
21
#include <string.h>
22
#include <stdio.h>
23
#include <stdbool.h>
24
#include <math.h>
25
#include <errno.h>
26
#include <sys/types.h>
27
#include <sys/stat.h>
28
#include <fcntl.h>
29
#include <assert.h>
30
31
#include <mpv/client.h>
32
33
#include "osdep/io.h"
34
#include "misc/rendezvous.h"
35
36
#include "event.h"
37
#include "input.h"
38
#include "keycodes.h"
39
#include "osdep/threads.h"
40
#include "osdep/timer.h"
41
#include "common/msg.h"
42
#include "common/global.h"
43
#include "options/m_config.h"
44
#include "options/m_option.h"
45
#include "options/path.h"
46
#include "mpv_talloc.h"
47
#include "options/options.h"
48
#include "player/external_files.h"
49
#include "misc/bstr.h"
50
#include "misc/node.h"
51
#include "stream/stream.h"
52
#include "common/common.h"
53
54
#if HAVE_COCOA
55
#include "osdep/mac/app_bridge.h"
56
#endif
57
58
8.72M
#define input_lock(ictx)    mp_mutex_lock(&ictx->mutex)
59
8.72M
#define input_unlock(ictx)  mp_mutex_unlock(&ictx->mutex)
60
61
143k
#define MP_MAX_KEY_DOWN 16
62
63
struct cmd_bind {
64
    int keys[MP_MAX_KEY_DOWN];
65
    int num_keys;
66
    char *cmd;
67
    char *location;     // filename/line number of definition
68
    char *desc;         // human readable description
69
    bool is_builtin;
70
    struct cmd_bind_section *owner;
71
};
72
73
struct cmd_bind_section {
74
    char *owner;
75
    struct cmd_bind *binds;
76
    int num_binds;
77
    bstr section;
78
    struct mp_rect mouse_area;  // set at runtime, if at all
79
    bool mouse_area_set;        // mouse_area is valid and should be tested
80
};
81
82
0
#define MP_MAX_SOURCES 10
83
84
struct active_section {
85
    bstr name;
86
    int flags;
87
};
88
89
struct cmd_queue {
90
    struct mp_cmd *first;
91
};
92
93
struct wheel_state {
94
    double dead_zone_accum;
95
    double unit_accum;
96
};
97
98
struct touch_point {
99
    int id;
100
    int x, y;
101
};
102
103
struct input_ctx {
104
    mp_mutex mutex;
105
    struct mp_log *log;
106
    struct mpv_global *global;
107
    struct m_config_cache *opts_cache;
108
    struct input_opts *opts;
109
110
    bool using_ar;
111
    bool using_cocoa_media_keys;
112
113
    // Autorepeat stuff
114
    short ar_state;
115
    int64_t last_ar;
116
117
    // history of key downs - the newest is in position 0
118
    int key_history[MP_MAX_KEY_DOWN];
119
    // key code of the last key that triggered MP_KEY_STATE_DOWN
120
    int last_key_down;
121
    int64_t last_key_down_time;
122
    struct mp_cmd *current_down_cmd;
123
124
    int last_doubleclick_key_down;
125
    double last_doubleclick_time;
126
127
    // VO dragging state
128
    bool dragging_button_down;
129
    int mouse_drag_x, mouse_drag_y;
130
    // Raw mouse position before transform
131
    int mouse_raw_x, mouse_raw_y;
132
133
    // Mouse position on the consumer side (as command.c sees it)
134
    int mouse_x, mouse_y;
135
    int mouse_hover;  // updated on mouse-enter/leave
136
    bstr mouse_section; // last section to receive mouse event
137
138
    // Mouse position on the producer side (as the VO sees it)
139
    // Unlike mouse_x/y, this can be used to resolve mouse click bindings.
140
    int mouse_vo_x, mouse_vo_y;
141
142
    bool mouse_mangle, mouse_src_mangle;
143
    struct mp_rect mouse_src, mouse_dst;
144
145
    // Wheel state (MP_WHEEL_*)
146
    struct wheel_state wheel_state_y; // MP_WHEEL_UP/MP_WHEEL_DOWN
147
    struct wheel_state wheel_state_x; // MP_WHEEL_LEFT/MP_WHEEL_RIGHT
148
    struct wheel_state *wheel_current; // The direction currently being scrolled
149
    double last_wheel_time; // mp_time_sec() of the last wheel event
150
151
    // List of command binding sections
152
    struct cmd_bind_section **sections;
153
    int num_sections;
154
155
    // List currently active command sections
156
    struct active_section *active_sections;
157
    int num_active_sections;
158
159
    // List currently active touch points
160
    struct touch_point *touch_points;
161
    int num_touch_points;
162
163
    // List of last dropped files
164
    int64_t dnd_ts;
165
    enum mp_dnd_action dnd_action;
166
    bstr *dropped_files;
167
    int num_dropped_files;
168
169
    int tablet_x, tablet_y;
170
    // Indicates tablet tools in proximity / close to tablet surface
171
    bool tablet_tool_in_proximity;
172
    bool tablet_tool_down;
173
    bool tablet_tool_stylus_btn1_pressed;
174
    bool tablet_tool_stylus_btn2_pressed;
175
    bool tablet_tool_stylus_btn3_pressed;
176
    bool tablet_pad_focus;
177
    bool tablet_pad_buttons_pressed[MP_MAX_TABLET_PAD_BUTTONS];
178
    int tablet_pad_buttons;
179
180
    unsigned int mouse_event_counter;
181
182
    struct mp_input_src *sources[MP_MAX_SOURCES];
183
    int num_sources;
184
185
    struct cmd_queue cmd_queue;
186
187
    void (*wakeup_cb)(void *ctx);
188
    void *wakeup_ctx;
189
};
190
191
static int parse_config(struct input_ctx *ictx, bool builtin, bstr data,
192
                        const char *location, const bstr restrict_section);
193
static void close_input_sources(struct input_ctx *ictx);
194
static bool test_mouse(struct input_ctx *ictx, int x, int y, int rej_flags);
195
196
#define OPT_BASE_STRUCT struct input_opts
197
struct input_opts {
198
    char *config_file;
199
    int doubleclick_time;
200
    // Maximum number of queued commands from keypresses (limit to avoid
201
    // repeated slow commands piling up)
202
    int key_fifo_size;
203
    // Autorepeat config (be aware of mp_input_set_repeat_info())
204
    int ar_delay;
205
    int ar_rate;
206
    int dragging_deadzone;
207
    bool use_alt_gr;
208
    bool use_gamepad;
209
    bool use_media_keys;
210
    bool default_bindings;
211
    bool builtin_bindings;
212
    bool builtin_dragging;
213
    bool enable_mouse_movements;
214
    bool vo_key_input;
215
    bool test;
216
    bool allow_win_drag;
217
    bool preprocess_wheel;
218
    bool touch_emulate_mouse;
219
    bool tablet_emulate_mouse;
220
};
221
222
const struct m_sub_options input_config = {
223
    .opts = (const m_option_t[]) {
224
        {"input-conf", OPT_STRING(config_file), .flags = M_OPT_FILE},
225
        {"input-ar-delay", OPT_INT(ar_delay)},
226
        {"input-ar-rate", OPT_INT(ar_rate)},
227
        {"input-keylist", OPT_PRINT(mp_print_key_list)},
228
        {"input-cmdlist", OPT_PRINT(mp_print_cmd_list)},
229
        {"input-default-bindings", OPT_BOOL(default_bindings)},
230
        {"input-builtin-bindings", OPT_BOOL(builtin_bindings)},
231
        {"input-builtin-dragging", OPT_BOOL(builtin_dragging)},
232
        {"input-test", OPT_BOOL(test)},
233
        {"input-doubleclick-time", OPT_INT(doubleclick_time),
234
         M_RANGE(0, 1000)},
235
        {"input-right-alt-gr", OPT_BOOL(use_alt_gr)},
236
        {"input-key-fifo-size", OPT_INT(key_fifo_size), M_RANGE(2, 65000)},
237
        {"input-cursor", OPT_BOOL(enable_mouse_movements)},
238
        {"input-vo-keyboard", OPT_BOOL(vo_key_input)},
239
        {"input-media-keys", OPT_BOOL(use_media_keys)},
240
        {"input-preprocess-wheel", OPT_BOOL(preprocess_wheel)},
241
        {"input-touch-emulate-mouse", OPT_BOOL(touch_emulate_mouse)},
242
        {"input-tablet-emulate-mouse", OPT_BOOL(tablet_emulate_mouse)},
243
        {"input-dragging-deadzone", OPT_INT(dragging_deadzone)},
244
#if HAVE_SDL2_GAMEPAD
245
        {"input-gamepad", OPT_BOOL(use_gamepad)},
246
#endif
247
        {"window-dragging", OPT_BOOL(allow_win_drag)},
248
        {0}
249
    },
250
    .size = sizeof(struct input_opts),
251
    .defaults = &(const struct input_opts){
252
        .key_fifo_size = 7,
253
        .doubleclick_time = 300,
254
        .ar_delay = 200,
255
        .ar_rate = 40,
256
        .dragging_deadzone = 3,
257
        .use_alt_gr = true,
258
        .enable_mouse_movements = true,
259
        .use_media_keys = true,
260
        .default_bindings = true,
261
        .builtin_bindings = true,
262
        .builtin_dragging = true,
263
        .vo_key_input = true,
264
        .allow_win_drag = true,
265
        .preprocess_wheel = true,
266
        .touch_emulate_mouse = true,
267
        .tablet_emulate_mouse = true,
268
    },
269
    .change_flags = UPDATE_INPUT,
270
};
271
272
static const char builtin_input_conf[] =
273
#include "etc/input.conf.inc"
274
;
275
276
static bool test_rect(struct mp_rect *rc, int x, int y)
277
0
{
278
0
    return x >= rc->x0 && y >= rc->y0 && x < rc->x1 && y < rc->y1;
279
0
}
280
281
static int queue_count_cmds(struct cmd_queue *queue)
282
0
{
283
0
    int res = 0;
284
0
    for (struct mp_cmd *cmd = queue->first; cmd; cmd = cmd->queue_next)
285
0
        res++;
286
0
    return res;
287
0
}
288
289
static void queue_remove(struct cmd_queue *queue, struct mp_cmd *cmd)
290
0
{
291
0
    struct mp_cmd **p_prev = &queue->first;
292
0
    while (*p_prev != cmd) {
293
0
        p_prev = &(*p_prev)->queue_next;
294
0
    }
295
    // if this fails, cmd was not in the queue
296
0
    mp_assert(*p_prev == cmd);
297
0
    *p_prev = cmd->queue_next;
298
0
}
299
300
static struct mp_cmd *queue_remove_head(struct cmd_queue *queue)
301
3.05M
{
302
3.05M
    struct mp_cmd *ret = queue->first;
303
3.05M
    if (ret)
304
0
        queue_remove(queue, ret);
305
3.05M
    return ret;
306
3.05M
}
307
308
static void queue_add_tail(struct cmd_queue *queue, struct mp_cmd *cmd)
309
0
{
310
0
    struct mp_cmd **p_prev = &queue->first;
311
0
    while (*p_prev)
312
0
        p_prev = &(*p_prev)->queue_next;
313
0
    *p_prev = cmd;
314
0
    cmd->queue_next = NULL;
315
0
}
316
317
static struct mp_cmd *queue_peek_tail(struct cmd_queue *queue)
318
0
{
319
0
    struct mp_cmd *cur = queue->first;
320
0
    while (cur && cur->queue_next)
321
0
        cur = cur->queue_next;
322
0
    return cur;
323
0
}
324
325
static void queue_cmd(struct input_ctx *ictx, mp_cmd_t *cmd)
326
0
{
327
0
    if (!cmd)
328
0
        return;
329
0
    queue_add_tail(&ictx->cmd_queue, cmd);
330
0
    mp_input_wakeup(ictx);
331
0
}
332
333
static void append_bind_info(struct input_ctx *ictx, char **pmsg,
334
                             struct cmd_bind *bind)
335
0
{
336
0
    char *msg = *pmsg;
337
0
    struct mp_cmd *cmd = mp_input_parse_cmd(ictx, bstr0(bind->cmd),
338
0
                                            bind->location);
339
0
    char *stripped = cmd ? cmd->original : bind->cmd;
340
0
    msg = talloc_asprintf_append(msg, " '%s'", stripped);
341
0
    if (!cmd)
342
0
        msg = talloc_asprintf_append(msg, " (invalid)");
343
0
    if (!bstr_equals0(bind->owner->section, "default"))
344
0
        msg = talloc_asprintf_append(msg, " in section {%.*s}",
345
0
                                     BSTR_P(bind->owner->section));
346
0
    msg = talloc_asprintf_append(msg, " in %s", bind->location);
347
0
    if (bind->is_builtin)
348
0
        msg = talloc_asprintf_append(msg, " (default)");
349
0
    talloc_free(cmd);
350
0
    *pmsg = msg;
351
0
}
352
353
static mp_cmd_t *handle_test(struct input_ctx *ictx, int code)
354
0
{
355
0
    if (code == MP_KEY_CLOSE_WIN) {
356
0
        MP_WARN(ictx,
357
0
            "CLOSE_WIN was received. This pseudo key can be remapped too,\n"
358
0
            "but --input-test will always quit when receiving it.\n");
359
0
        const char *args[] = {"quit", NULL};
360
0
        mp_cmd_t *res = mp_input_parse_cmd_strv(ictx->log, args);
361
0
        return res;
362
0
    }
363
364
0
    char *key_buf = mp_input_get_key_combo_name(&code, 1);
365
0
    char *msg = talloc_asprintf(NULL, "Key %s is bound to:\n", key_buf);
366
0
    talloc_free(key_buf);
367
368
0
    int count = 0;
369
0
    for (int n = 0; n < ictx->num_sections; n++) {
370
0
        struct cmd_bind_section *bs = ictx->sections[n];
371
372
0
        for (int i = 0; i < bs->num_binds; i++) {
373
0
            if (bs->binds[i].num_keys && bs->binds[i].keys[0] == code) {
374
0
                count++;
375
0
                if (count > 1)
376
0
                    msg = talloc_asprintf_append(msg, "\n");
377
0
                msg = talloc_asprintf_append(msg, "%d. ", count);
378
0
                append_bind_info(ictx, &msg, &bs->binds[i]);
379
0
            }
380
0
        }
381
0
    }
382
383
0
    if (!count)
384
0
        msg = talloc_asprintf_append(msg, "(nothing)");
385
386
0
    MP_INFO(ictx, "%s\n", msg);
387
0
    const char *args[] = {"show-text", msg, NULL};
388
0
    mp_cmd_t *res = mp_input_parse_cmd_strv(ictx->log, args);
389
0
    talloc_free(msg);
390
0
    return res;
391
0
}
392
393
static struct cmd_bind_section *find_section(struct input_ctx *ictx,
394
                                             bstr section)
395
588k
{
396
971k
    for (int n = 0; n < ictx->num_sections; n++) {
397
739k
        struct cmd_bind_section *bs = ictx->sections[n];
398
739k
        if (bstr_equals(section, bs->section))
399
356k
            return bs;
400
739k
    }
401
231k
    return NULL;
402
588k
}
403
404
static struct cmd_bind_section *get_bind_section(struct input_ctx *ictx,
405
                                                 bstr section)
406
588k
{
407
588k
    if (section.len == 0)
408
353k
        section = bstr0("default");
409
588k
    struct cmd_bind_section *bind_section = find_section(ictx, section);
410
588k
    if (bind_section)
411
356k
        return bind_section;
412
231k
    bind_section = talloc_ptrtype(ictx, bind_section);
413
231k
    *bind_section = (struct cmd_bind_section) {
414
231k
        .section = bstrdup(bind_section, section),
415
231k
        .mouse_area = {INT_MIN, INT_MIN, INT_MAX, INT_MAX},
416
231k
        .mouse_area_set = true,
417
231k
    };
418
231k
    MP_TARRAY_APPEND(ictx, ictx->sections, ictx->num_sections, bind_section);
419
231k
    return bind_section;
420
588k
}
421
422
static void key_buf_add(int *buf, int code)
423
0
{
424
0
    for (int n = MP_MAX_KEY_DOWN - 1; n > 0; n--)
425
0
        buf[n] = buf[n - 1];
426
0
    buf[0] = code;
427
0
}
428
429
static struct cmd_bind *find_bind_for_key_section(struct input_ctx *ictx,
430
                                                  bstr section, int code)
431
0
{
432
0
    struct cmd_bind_section *bs = get_bind_section(ictx, section);
433
434
0
    if (!bs->num_binds)
435
0
        return NULL;
436
437
0
    int keys[MP_MAX_KEY_DOWN];
438
0
    memcpy(keys, ictx->key_history, sizeof(keys));
439
0
    key_buf_add(keys, code);
440
441
0
    struct cmd_bind *best = NULL;
442
443
    // Prefer user-defined keys over builtin bindings
444
0
    for (int builtin = 0; builtin < 2; builtin++) {
445
0
        if (builtin && !ictx->opts->default_bindings)
446
0
            break;
447
0
        for (int n = 0; n < bs->num_binds; n++) {
448
0
            if (bs->binds[n].is_builtin == (bool)builtin) {
449
0
                struct cmd_bind *b = &bs->binds[n];
450
                // we have: keys=[key2 key1 keyX ...]
451
                // and: b->keys=[key1 key2] (and may be just a prefix)
452
0
                for (int i = 0; i < b->num_keys; i++) {
453
0
                    if (b->keys[i] != keys[b->num_keys - 1 - i])
454
0
                        goto skip;
455
0
                }
456
0
                if (!best || b->num_keys > best->num_keys)
457
0
                    best = b;
458
0
            skip: ;
459
0
            }
460
0
        }
461
0
    }
462
0
    return best;
463
0
}
464
465
static struct cmd_bind *find_any_bind_for_key(struct input_ctx *ictx,
466
                                              bstr force_section, int code)
467
0
{
468
0
    if (force_section.len)
469
0
        return find_bind_for_key_section(ictx, force_section, code);
470
471
0
    bool use_mouse = MP_KEY_DEPENDS_ON_MOUSE_POS(code);
472
473
    // First look whether a mouse section is capturing all mouse input
474
    // exclusively (regardless of the active section stack order).
475
0
    if (use_mouse && MP_KEY_IS_MOUSE_BTN_SINGLE(ictx->last_key_down) &&
476
0
        !MP_KEY_IS_MOUSE_BTN_DBL(code))
477
0
    {
478
0
        struct cmd_bind *bind =
479
0
            find_bind_for_key_section(ictx, ictx->mouse_section, code);
480
0
        if (bind)
481
0
            return bind;
482
0
    }
483
484
0
    struct cmd_bind *best_bind = NULL;
485
0
    for (int i = ictx->num_active_sections - 1; i >= 0; i--) {
486
0
        struct active_section *s = &ictx->active_sections[i];
487
0
        struct cmd_bind *bind = find_bind_for_key_section(ictx, s->name, code);
488
0
        if (bind) {
489
0
            struct cmd_bind_section *bs = bind->owner;
490
0
            if (!use_mouse || (bs->mouse_area_set && test_rect(&bs->mouse_area,
491
0
                                                               ictx->mouse_vo_x,
492
0
                                                               ictx->mouse_vo_y)))
493
0
            {
494
0
                if (!best_bind || bind->num_keys > best_bind->num_keys ||
495
0
                    (best_bind->is_builtin && !bind->is_builtin &&
496
0
                     bind->num_keys == best_bind->num_keys))
497
0
                {
498
0
                    best_bind = bind;
499
0
                }
500
0
            }
501
0
        }
502
0
        if (s->flags & MP_INPUT_EXCLUSIVE)
503
0
            break;
504
0
        if (best_bind && (s->flags & MP_INPUT_ON_TOP))
505
0
            break;
506
0
    }
507
508
0
    return best_bind;
509
0
}
510
511
static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, bstr force_section,
512
                                   int code)
513
0
{
514
0
    if (ictx->opts->test)
515
0
        return handle_test(ictx, code);
516
517
0
    struct cmd_bind *cmd = NULL;
518
519
0
    if (MP_KEY_IS_UNICODE(code))
520
0
        cmd = find_any_bind_for_key(ictx, force_section, MP_KEY_ANY_UNICODE);
521
0
    if (!cmd)
522
0
        cmd = find_any_bind_for_key(ictx, force_section, code);
523
0
    if (!cmd)
524
0
        cmd = find_any_bind_for_key(ictx, force_section, MP_KEY_UNMAPPED);
525
0
    if (!cmd) {
526
0
        if (code == MP_KEY_CLOSE_WIN)
527
0
            return mp_input_parse_cmd_strv(ictx->log, (const char*[]){"quit", 0});
528
0
        int msgl = MSGL_WARN;
529
0
        if (MP_KEY_IS_MOUSE_MOVE(code))
530
0
            msgl = MSGL_TRACE;
531
0
        char *key_buf = mp_input_get_key_combo_name(&code, 1);
532
0
        MP_MSG(ictx, msgl, "No key binding found for key '%s'.\n", key_buf);
533
0
        talloc_free(key_buf);
534
0
        return NULL;
535
0
    }
536
0
    mp_cmd_t *ret = mp_input_parse_cmd(ictx, bstr0(cmd->cmd), cmd->location);
537
0
    if (ret) {
538
0
        ret->input_section = cmd->owner->section;
539
0
        ret->key_name = talloc_steal(ret, mp_input_get_key_combo_name(&code, 1));
540
0
        MP_TRACE(ictx, "key '%s' -> '%s' in '%.*s'\n",
541
0
                 ret->key_name, cmd->cmd, BSTR_P(ret->input_section));
542
0
        if (MP_KEY_IS_UNICODE(code)) {
543
0
            bstr text = {0};
544
0
            mp_append_utf8_bstr(ret, &text, code);
545
0
            ret->key_text = text.start;
546
0
        }
547
0
        ret->is_mouse_button = code & MP_KEY_EMIT_ON_UP;
548
0
    } else {
549
0
        char *key_buf = mp_input_get_key_combo_name(&code, 1);
550
0
        MP_ERR(ictx, "Invalid command for key binding '%s': '%s'\n",
551
0
               key_buf, cmd->cmd);
552
0
        talloc_free(key_buf);
553
0
    }
554
0
    return ret;
555
0
}
556
557
static void update_mouse_section(struct input_ctx *ictx)
558
0
{
559
0
    struct cmd_bind *bind =
560
0
        find_any_bind_for_key(ictx, (bstr){0}, MP_KEY_MOUSE_MOVE);
561
562
0
    bstr new_section = bind ? bind->owner->section : bstr0("default");
563
564
0
    bstr old = ictx->mouse_section;
565
0
    ictx->mouse_section = new_section;
566
567
0
    if (!bstr_equals(old, ictx->mouse_section)) {
568
0
        MP_TRACE(ictx, "input: switch section %.*s -> %.*s\n",
569
0
                 BSTR_P(old), BSTR_P(ictx->mouse_section));
570
0
        queue_cmd(ictx, get_cmd_from_keys(ictx, old, MP_KEY_MOUSE_LEAVE));
571
0
    }
572
0
}
573
574
// Called when the currently held-down key is released. This (usually) sends
575
// the key-up version of the command associated with the keys that were held
576
// down.
577
// If the drop_current parameter is set to true, then don't send the key-up
578
// command. Unless we've already sent a key-down event, in which case the
579
// input receiver (the player) must get a key-up event, or it would get stuck
580
// thinking a key is still held down. In this case, mark the command as
581
// canceled so that it can be distinguished from a normally triggered command.
582
static void release_down_cmd(struct input_ctx *ictx, bool drop_current)
583
0
{
584
0
    if (ictx->current_down_cmd && ictx->current_down_cmd->emit_on_up &&
585
0
        (!drop_current || ictx->current_down_cmd->def->on_updown))
586
0
    {
587
0
        memset(ictx->key_history, 0, sizeof(ictx->key_history));
588
0
        ictx->current_down_cmd->is_up = true;
589
0
        if (drop_current)
590
0
            ictx->current_down_cmd->canceled = true;
591
0
        queue_cmd(ictx, ictx->current_down_cmd);
592
0
    } else {
593
0
        talloc_free(ictx->current_down_cmd);
594
0
    }
595
0
    ictx->current_down_cmd = NULL;
596
0
    ictx->last_key_down = 0;
597
0
    ictx->last_key_down_time = 0;
598
0
    ictx->ar_state = -1;
599
0
    update_mouse_section(ictx);
600
0
}
601
602
// We don't want it to append to the command queue indefinitely, because that
603
// could lead to situations where recovery would take too long.
604
static bool should_drop_cmd(struct input_ctx *ictx, struct mp_cmd *cmd)
605
0
{
606
0
    struct cmd_queue *queue = &ictx->cmd_queue;
607
0
    return queue_count_cmds(queue) >= ictx->opts->key_fifo_size;
608
0
}
609
610
static struct mp_cmd *resolve_key(struct input_ctx *ictx, int code)
611
0
{
612
0
    update_mouse_section(ictx);
613
0
    struct mp_cmd *cmd = get_cmd_from_keys(ictx, (bstr){0}, code);
614
0
    key_buf_add(ictx->key_history, code);
615
0
    if (cmd && !cmd->def->is_ignore && !should_drop_cmd(ictx, cmd))
616
0
        return cmd;
617
0
    talloc_free(cmd);
618
0
    return NULL;
619
0
}
620
621
static void interpret_key(struct input_ctx *ictx, int code, double scale,
622
                          int scale_units)
623
0
{
624
0
    int state = code & (MP_KEY_STATE_DOWN | MP_KEY_STATE_UP);
625
0
    bool no_emit = code & MP_KEY_STATE_SET_ONLY;
626
0
    code = code & ~(unsigned)(state | MP_KEY_STATE_SET_ONLY);
627
628
0
    if (mp_msg_test(ictx->log, MSGL_TRACE)) {
629
0
        char *key = mp_input_get_key_name(code);
630
0
        MP_TRACE(ictx, "key code=%#x '%s'%s%s\n",
631
0
                 code, key, (state & MP_KEY_STATE_DOWN) ? " down" : "",
632
0
                 (state & MP_KEY_STATE_UP) ? " up" : "");
633
0
        talloc_free(key);
634
0
    }
635
636
0
    if (MP_KEY_DEPENDS_ON_MOUSE_POS(code & ~MP_KEY_MODIFIER_MASK)) {
637
0
        ictx->mouse_event_counter++;
638
0
        mp_input_wakeup(ictx);
639
0
    }
640
641
0
    struct mp_cmd *cmd = NULL;
642
643
0
    if (state == MP_KEY_STATE_DOWN) {
644
        // Protect against VOs which send STATE_DOWN with autorepeat
645
0
        if (ictx->last_key_down == code)
646
0
            return;
647
        // Cancel current down-event (there can be only one)
648
0
        release_down_cmd(ictx, true);
649
0
        cmd = resolve_key(ictx, code);
650
0
        if (cmd) {
651
0
            cmd->is_up_down = true;
652
0
            cmd->emit_on_up = (code & MP_KEY_EMIT_ON_UP) || cmd->def->on_updown;
653
0
            ictx->current_down_cmd = mp_cmd_clone(cmd);
654
0
        }
655
0
        ictx->last_key_down = code;
656
0
        ictx->last_key_down_time = mp_time_ns();
657
0
        ictx->ar_state = 0;
658
0
        mp_input_wakeup(ictx); // possibly start timer for autorepeat
659
0
    } else if (state == MP_KEY_STATE_UP) {
660
        // Most VOs send RELEASE_ALL anyway
661
0
        release_down_cmd(ictx, false);
662
0
    } else {
663
        // Press of key with no separate down/up events
664
        // Mixing press events and up/down with the same key is not supported,
665
        // and input sources shouldn't do this, but can happen anyway if
666
        // multiple input sources interfere with each other.
667
0
        if (ictx->last_key_down == code)
668
0
            release_down_cmd(ictx, false);
669
0
        cmd = resolve_key(ictx, code);
670
0
    }
671
672
0
    if (!cmd)
673
0
        return;
674
675
    // Don't emit a command on key-down if the key is designed to emit commands
676
    // on key-up (like mouse buttons), or setting key state only without emitting commands.
677
    // Also, if the command specifically should be sent both on key down and key up,
678
    // still emit the command.
679
0
    if ((cmd->emit_on_up && !cmd->def->on_updown) || no_emit) {
680
0
        talloc_free(cmd);
681
0
        return;
682
0
    }
683
684
0
    memset(ictx->key_history, 0, sizeof(ictx->key_history));
685
686
0
    if (mp_input_is_scalable_cmd(cmd)) {
687
0
        cmd->scale = scale;
688
0
        cmd->scale_units = scale_units;
689
0
        queue_cmd(ictx, cmd);
690
0
    } else {
691
        // Non-scalable commands won't understand cmd->scale, so synthesize
692
        // multiple commands with cmd->scale = 1
693
0
        cmd->scale = 1;
694
0
        cmd->scale_units = 1;
695
        // Avoid spamming the player with too many commands
696
0
        scale_units = MPMIN(scale_units, 20);
697
0
        for (int i = 0; i < scale_units - 1; i++)
698
0
            queue_cmd(ictx, mp_cmd_clone(cmd));
699
0
        if (scale_units) {
700
0
            queue_cmd(ictx, cmd);
701
0
        } else {
702
0
            talloc_free(cmd);
703
0
        }
704
0
    }
705
0
}
706
707
// Pre-processing for MP_WHEEL_* events. If this returns false, the caller
708
// should discard the event.
709
static bool process_wheel(struct input_ctx *ictx, int code, double *scale,
710
                          int *scale_units)
711
0
{
712
    // Size of the deadzone in scroll units. The user must scroll at least this
713
    // much in any direction before their scroll is registered.
714
0
    static const double DEADZONE_DIST = 0.125;
715
    // The deadzone accumulator is reset if no scrolls happened in this many
716
    // seconds, e.g. the user is assumed to have finished scrolling.
717
0
    static const double DEADZONE_SCROLL_TIME = 0.2;
718
    // The scale_units accumulator is reset if no scrolls happened in this many
719
    // seconds. This value should be fairly large, so commands will still be
720
    // sent when the user scrolls slowly.
721
0
    static const double UNIT_SCROLL_TIME = 0.5;
722
723
    // Determine which direction is being scrolled
724
0
    double dir;
725
0
    struct wheel_state *state;
726
0
    switch (code) {
727
0
    case MP_WHEEL_UP:    dir = -1; state = &ictx->wheel_state_y; break;
728
0
    case MP_WHEEL_DOWN:  dir = +1; state = &ictx->wheel_state_y; break;
729
0
    case MP_WHEEL_LEFT:  dir = -1; state = &ictx->wheel_state_x; break;
730
0
    case MP_WHEEL_RIGHT: dir = +1; state = &ictx->wheel_state_x; break;
731
0
    default:
732
0
        return true;
733
0
    }
734
735
    // Reset accumulators if it's determined that the user finished scrolling
736
0
    double now = mp_time_sec();
737
0
    if (now > ictx->last_wheel_time + DEADZONE_SCROLL_TIME) {
738
0
        ictx->wheel_current = NULL;
739
0
        ictx->wheel_state_y.dead_zone_accum = 0;
740
0
        ictx->wheel_state_x.dead_zone_accum = 0;
741
0
    }
742
0
    if (now > ictx->last_wheel_time + UNIT_SCROLL_TIME) {
743
0
        ictx->wheel_state_y.unit_accum = 0;
744
0
        ictx->wheel_state_x.unit_accum = 0;
745
0
    }
746
0
    ictx->last_wheel_time = now;
747
748
    // Process wheel deadzone. A lot of touchpad drivers don't filter scroll
749
    // input, which makes it difficult for the user to send WHEEL_UP/DOWN
750
    // without accidentally triggering WHEEL_LEFT/RIGHT. We try to fix this by
751
    // implementing a deadzone. When the value of either direction breaks out
752
    // of the deadzone, events from the other direction will be ignored until
753
    // the user finishes scrolling.
754
0
    if (ictx->wheel_current == NULL) {
755
0
        state->dead_zone_accum += *scale * dir;
756
0
        if (state->dead_zone_accum * dir > DEADZONE_DIST) {
757
0
            ictx->wheel_current = state;
758
0
            *scale = state->dead_zone_accum * dir;
759
0
        }
760
0
    }
761
0
    if (ictx->wheel_current != state)
762
0
        return false;
763
764
    // Determine scale_units. This is incremented every time the accumulated
765
    // scale value crosses 1.0. Non-scalable input commands will be ran that
766
    // many times.
767
0
    state->unit_accum += *scale * dir;
768
0
    *scale_units = trunc(state->unit_accum * dir);
769
0
    state->unit_accum -= *scale_units * dir;
770
0
    return true;
771
0
}
772
773
static void feed_key(struct input_ctx *ictx, int code, double scale,
774
                              bool force_mouse)
775
0
{
776
0
    struct input_opts *opts = ictx->opts;
777
778
0
    code = mp_normalize_keycode(code);
779
0
    int unmod = code & ~MP_KEY_MODIFIER_MASK;
780
0
    if (code == MP_INPUT_RELEASE_ALL) {
781
0
        MP_TRACE(ictx, "release all\n");
782
0
        release_down_cmd(ictx, false);
783
0
        ictx->dragging_button_down = false;
784
0
        return;
785
0
    }
786
0
    if (code == MP_TOUCH_RELEASE_ALL) {
787
0
        MP_TRACE(ictx, "release all touch\n");
788
0
        ictx->num_touch_points = 0;
789
0
        return;
790
0
    }
791
0
    if (!opts->enable_mouse_movements && MP_KEY_IS_MOUSE(unmod) && !force_mouse)
792
0
        return;
793
0
    if (unmod == MP_KEY_MOUSE_LEAVE || unmod == MP_KEY_MOUSE_ENTER) {
794
0
        ictx->mouse_hover = unmod == MP_KEY_MOUSE_ENTER;
795
0
        update_mouse_section(ictx);
796
797
0
        mp_cmd_t *cmd = get_cmd_from_keys(ictx, (bstr){0}, code);
798
0
        if (!cmd)  // queue dummy cmd so that mouse-pos can notify observers
799
0
            cmd = mp_input_parse_cmd(ictx, bstr0("ignore"), "<internal>");
800
0
        if (cmd)
801
0
            cmd->notify_event = true;
802
0
        queue_cmd(ictx, cmd);
803
0
        return;
804
0
    }
805
0
    double now = mp_time_sec();
806
    // ignore system double-click if we generate these events ourselves
807
0
    if (!force_mouse && opts->doubleclick_time && MP_KEY_IS_MOUSE_BTN_DBL(unmod))
808
0
        return;
809
0
    int units = 1;
810
0
    if (MP_KEY_IS_WHEEL(unmod) && opts->preprocess_wheel && !process_wheel(ictx, unmod, &scale, &units))
811
0
        return;
812
0
    interpret_key(ictx, code, scale, units);
813
0
    if (code & MP_KEY_STATE_DOWN) {
814
0
        code &= ~MP_KEY_STATE_DOWN;
815
0
        if (ictx->last_doubleclick_key_down == code &&
816
0
            now - ictx->last_doubleclick_time < opts->doubleclick_time / 1000.0 &&
817
0
            code >= MP_MBTN_LEFT && code <= MP_MBTN_RIGHT)
818
0
        {
819
0
            now = 0;
820
0
            interpret_key(ictx, code - MP_MBTN_BASE + MP_MBTN_DBL_BASE,
821
0
                          1, 1);
822
0
        } else if (code == MP_MBTN_LEFT && ictx->opts->allow_win_drag &&
823
0
                   !test_mouse(ictx, ictx->mouse_vo_x, ictx->mouse_vo_y, MP_INPUT_ALLOW_VO_DRAGGING))
824
0
        {
825
            // This is a mouse left button down event which isn't part of a double-click,
826
            // and the mouse is on an input section which allows VO dragging.
827
            // Mark the dragging mouse button down in this case.
828
0
            ictx->dragging_button_down = true;
829
            // Store the current mouse position for deadzone handling.
830
0
            ictx->mouse_drag_x = ictx->mouse_raw_x;
831
0
            ictx->mouse_drag_y = ictx->mouse_raw_y;
832
0
        }
833
0
        ictx->last_doubleclick_key_down = code;
834
0
        ictx->last_doubleclick_time = now;
835
0
    }
836
0
    if (code & MP_KEY_STATE_UP) {
837
0
        code &= ~MP_KEY_STATE_UP;
838
0
        if (code == MP_MBTN_LEFT) {
839
            // This is a mouse left button up event. Mark the dragging mouse button up.
840
0
            ictx->dragging_button_down = false;
841
0
        }
842
0
    }
843
0
}
844
845
void mp_input_put_key(struct input_ctx *ictx, int code)
846
0
{
847
0
    input_lock(ictx);
848
0
    feed_key(ictx, code, 1, false);
849
0
    input_unlock(ictx);
850
0
}
851
852
void mp_input_put_key_artificial(struct input_ctx *ictx, int code, double value)
853
0
{
854
0
    if (value == 0.0)
855
0
        return;
856
0
    input_lock(ictx);
857
0
    feed_key(ictx, code, value, true);
858
0
    input_unlock(ictx);
859
0
}
860
861
void mp_input_put_key_utf8(struct input_ctx *ictx, int mods, struct bstr t)
862
0
{
863
0
    if (!t.len)
864
0
        return;
865
0
    input_lock(ictx);
866
0
    while (t.len) {
867
0
        int code = bstr_decode_utf8(t, &t);
868
0
        if (code < 0)
869
0
            break;
870
0
        feed_key(ictx, code | mods, 1, false);
871
0
    }
872
0
    input_unlock(ictx);
873
0
}
874
875
void mp_input_put_wheel(struct input_ctx *ictx, int direction, double value)
876
0
{
877
0
    if (value == 0.0)
878
0
        return;
879
0
    input_lock(ictx);
880
0
    feed_key(ictx, direction, value, false);
881
0
    input_unlock(ictx);
882
0
}
883
884
void mp_input_set_mouse_transform(struct input_ctx *ictx, struct mp_rect *dst,
885
                                  struct mp_rect *src)
886
91.6k
{
887
91.6k
    input_lock(ictx);
888
91.6k
    ictx->mouse_mangle = dst || src;
889
91.6k
    if (ictx->mouse_mangle) {
890
0
        ictx->mouse_dst = *dst;
891
0
        ictx->mouse_src_mangle = !!src;
892
0
        if (ictx->mouse_src_mangle)
893
0
            ictx->mouse_src = *src;
894
0
    }
895
91.6k
    input_unlock(ictx);
896
91.6k
}
897
898
bool mp_input_mouse_enabled(struct input_ctx *ictx)
899
0
{
900
0
    input_lock(ictx);
901
0
    bool r = ictx->opts->enable_mouse_movements;
902
0
    input_unlock(ictx);
903
0
    return r;
904
0
}
905
906
bool mp_input_vo_keyboard_enabled(struct input_ctx *ictx)
907
0
{
908
0
    input_lock(ictx);
909
0
    bool r = ictx->opts->vo_key_input;
910
0
    input_unlock(ictx);
911
0
    return r;
912
0
}
913
914
static void set_mouse_pos(struct input_ctx *ictx, int x, int y, bool quiet)
915
0
{
916
0
    MP_TRACE(ictx, "mouse move %d/%d\n", x, y);
917
918
0
    if (ictx->mouse_raw_x == x && ictx->mouse_raw_y == y) {
919
0
        return;
920
0
    }
921
0
    ictx->mouse_raw_x = x;
922
0
    ictx->mouse_raw_y = y;
923
924
0
    if (ictx->mouse_mangle) {
925
0
        struct mp_rect *src = &ictx->mouse_src;
926
0
        struct mp_rect *dst = &ictx->mouse_dst;
927
0
        x = MPCLAMP(x, dst->x0, dst->x1) - dst->x0;
928
0
        y = MPCLAMP(y, dst->y0, dst->y1) - dst->y0;
929
0
        if (ictx->mouse_src_mangle) {
930
0
            x = x * 1.0 / (dst->x1 - dst->x0) * (src->x1 - src->x0) + src->x0;
931
0
            y = y * 1.0 / (dst->y1 - dst->y0) * (src->y1 - src->y0) + src->y0;
932
0
        }
933
0
        MP_TRACE(ictx, "-> %d/%d\n", x, y);
934
0
    }
935
936
0
    if (!quiet)
937
0
        ictx->mouse_event_counter++;
938
0
    ictx->mouse_vo_x = x;
939
0
    ictx->mouse_vo_y = y;
940
941
0
    update_mouse_section(ictx);
942
0
    struct mp_cmd *cmd = get_cmd_from_keys(ictx, (bstr){0}, MP_KEY_MOUSE_MOVE);
943
0
    if (!cmd)
944
0
        cmd = mp_input_parse_cmd(ictx, bstr0("ignore"), "<internal>");
945
946
0
    if (cmd) {
947
0
        cmd->mouse_move = true;
948
0
        cmd->notify_event = true;
949
0
        cmd->mouse_x = x;
950
0
        cmd->mouse_y = y;
951
0
        if (should_drop_cmd(ictx, cmd)) {
952
0
            talloc_free(cmd);
953
0
        } else {
954
            // Coalesce with previous mouse move events (i.e. replace it)
955
0
            struct mp_cmd *tail = queue_peek_tail(&ictx->cmd_queue);
956
0
            if (tail && tail->mouse_move) {
957
0
                queue_remove(&ictx->cmd_queue, tail);
958
0
                talloc_free(tail);
959
0
            }
960
0
            queue_cmd(ictx, cmd);
961
0
        }
962
0
    }
963
964
0
    bool mouse_outside_dragging_deadzone =
965
0
        abs(ictx->mouse_raw_x - ictx->mouse_drag_x) >= ictx->opts->dragging_deadzone ||
966
0
        abs(ictx->mouse_raw_y - ictx->mouse_drag_y) >= ictx->opts->dragging_deadzone;
967
0
    if (ictx->dragging_button_down && mouse_outside_dragging_deadzone &&
968
0
        ictx->opts->builtin_dragging)
969
0
    {
970
        // Begin built-in VO dragging if the mouse moves while the dragging button is down.
971
0
        ictx->dragging_button_down = false;
972
        // Prevent activation of MBTN_LEFT key binding if VO dragging begins.
973
0
        release_down_cmd(ictx, true);
974
        // Prevent activation of MBTN_LEFT_DBL if VO dragging begins.
975
0
        ictx->last_doubleclick_time = 0;
976
0
        mp_cmd_t *drag_cmd = mp_input_parse_cmd(ictx, bstr0("begin-vo-dragging"), "<internal>");
977
0
        queue_cmd(ictx, drag_cmd);
978
0
    }
979
0
}
980
981
void mp_input_set_mouse_pos_artificial(struct input_ctx *ictx, int x, int y)
982
0
{
983
0
    input_lock(ictx);
984
0
    set_mouse_pos(ictx, x, y, false);
985
0
    input_unlock(ictx);
986
0
}
987
988
void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y, bool quiet)
989
0
{
990
0
    input_lock(ictx);
991
0
    if (ictx->opts->enable_mouse_movements)
992
0
        set_mouse_pos(ictx, x, y, quiet);
993
0
    input_unlock(ictx);
994
0
}
995
996
static int find_touch_point_index(struct input_ctx *ictx, int id)
997
0
{
998
0
    for (int i = 0; i < ictx->num_touch_points; i++) {
999
0
        if (ictx->touch_points[i].id == id)
1000
0
            return i;
1001
0
    }
1002
0
    return -1;
1003
0
}
1004
1005
static void notify_event_update(struct input_ctx *ictx)
1006
0
{
1007
    // queue dummy cmd so that touch-pos can notify observers
1008
0
    mp_cmd_t *cmd = mp_input_parse_cmd(ictx, bstr0("ignore"), "<internal>");
1009
0
    if (cmd)
1010
0
        cmd->notify_event = true;
1011
0
    queue_cmd(ictx, cmd);
1012
0
}
1013
1014
static void update_touch_point(struct input_ctx *ictx, int idx, int id, int x, int y)
1015
0
{
1016
0
    MP_TRACE(ictx, "Touch point %d update (id %d) %d/%d\n",
1017
0
             idx, id, x, y);
1018
0
    if (ictx->touch_points[idx].x == x && ictx->touch_points[idx].y == y)
1019
0
        return;
1020
0
    ictx->touch_points[idx].x = x;
1021
0
    ictx->touch_points[idx].y = y;
1022
    // Emulate mouse input from the primary touch point (the first one added)
1023
0
    if (ictx->opts->touch_emulate_mouse && idx == 0)
1024
0
        set_mouse_pos(ictx, x, y, false);
1025
0
    notify_event_update(ictx);
1026
0
}
1027
1028
void mp_input_add_touch_point(struct input_ctx *ictx, int id, int x, int y)
1029
0
{
1030
0
    input_lock(ictx);
1031
0
    int idx = find_touch_point_index(ictx, id);
1032
0
    if (idx != -1) {
1033
0
        MP_WARN(ictx, "Touch point %d (id %d) already exists! Treat as update.\n",
1034
0
                idx, id);
1035
0
        update_touch_point(ictx, idx, id, x, y);
1036
0
    } else {
1037
0
        MP_TRACE(ictx, "Touch point %d add (id %d) %d/%d\n",
1038
0
                 ictx->num_touch_points, id, x, y);
1039
0
        MP_TARRAY_APPEND(ictx, ictx->touch_points, ictx->num_touch_points,
1040
0
                         (struct touch_point){id, x, y});
1041
        // Emulate MBTN_LEFT down if this is the only touch point
1042
0
        if (ictx->opts->touch_emulate_mouse && ictx->num_touch_points == 1) {
1043
0
            set_mouse_pos(ictx, x, y, false);
1044
0
            feed_key(ictx, MP_MBTN_LEFT | MP_KEY_STATE_DOWN, 1, false);
1045
0
        }
1046
0
        notify_event_update(ictx);
1047
0
    }
1048
0
    input_unlock(ictx);
1049
0
}
1050
1051
void mp_input_update_touch_point(struct input_ctx *ictx, int id, int x, int y)
1052
0
{
1053
0
    input_lock(ictx);
1054
0
    int idx = find_touch_point_index(ictx, id);
1055
0
    if (idx != -1) {
1056
0
        update_touch_point(ictx, idx, id, x, y);
1057
0
    } else {
1058
0
        MP_WARN(ictx, "Touch point id %d does not exist!\n", id);
1059
0
    }
1060
0
    input_unlock(ictx);
1061
0
}
1062
1063
void mp_input_remove_touch_point(struct input_ctx *ictx, int id)
1064
0
{
1065
0
    input_lock(ictx);
1066
0
    int idx = find_touch_point_index(ictx, id);
1067
0
    if (idx != -1) {
1068
0
        MP_TRACE(ictx, "Touch point %d remove (id %d)\n", idx, id);
1069
0
        MP_TARRAY_REMOVE_AT(ictx->touch_points, ictx->num_touch_points, idx);
1070
        // Emulate MBTN_LEFT up if there are no touch points left
1071
0
        if (ictx->opts->touch_emulate_mouse && ictx->num_touch_points == 0)
1072
0
            feed_key(ictx, MP_MBTN_LEFT | MP_KEY_STATE_UP, 1, false);
1073
0
        notify_event_update(ictx);
1074
0
    }
1075
0
    input_unlock(ictx);
1076
0
}
1077
1078
int mp_input_get_touch_pos(struct input_ctx *ictx, int count, int *x, int *y, int *id)
1079
485
{
1080
485
    input_lock(ictx);
1081
485
    int num_touch_points = ictx->num_touch_points;
1082
485
    for (int i = 0; i < MPMIN(num_touch_points, count); i++) {
1083
0
        x[i] = ictx->touch_points[i].x;
1084
0
        y[i] = ictx->touch_points[i].y;
1085
0
        id[i] = ictx->touch_points[i].id;
1086
0
    }
1087
485
    input_unlock(ictx);
1088
485
    return num_touch_points;
1089
485
}
1090
1091
static void notify_tablet_update(struct input_ctx *ictx)
1092
0
{
1093
    // queue dummy cmd so that tablet-pos can notify observers
1094
0
    mp_cmd_t *cmd = mp_input_parse_cmd(ictx, bstr0("ignore"), "<internal>");
1095
0
    if (cmd)
1096
0
        cmd->notify_event = true;
1097
0
    queue_cmd(ictx, cmd);
1098
0
}
1099
1100
void mp_input_set_tablet_tool_in_proximity(struct input_ctx *ictx, bool in_proximity)
1101
0
{
1102
0
    MP_TRACE(ictx, "tablet tool proximity %s\n", in_proximity ? "in" : "out");
1103
1104
0
    input_lock(ictx);
1105
0
    ictx->tablet_tool_in_proximity = in_proximity;
1106
0
    if (!in_proximity) {
1107
0
        ictx->tablet_tool_down = false;
1108
0
        ictx->tablet_tool_stylus_btn1_pressed = false;
1109
0
        ictx->tablet_tool_stylus_btn2_pressed = false;
1110
0
        ictx->tablet_tool_stylus_btn3_pressed = false;
1111
0
    }
1112
0
    notify_tablet_update(ictx);
1113
0
    input_unlock(ictx);
1114
0
}
1115
1116
void mp_input_tablet_tool_down(struct input_ctx *ictx)
1117
0
{
1118
0
    MP_TRACE(ictx, "tablet tool down\n");
1119
1120
0
    input_lock(ictx);
1121
0
    ictx->tablet_tool_down = true;
1122
0
    if (ictx->opts->tablet_emulate_mouse && ictx->tablet_tool_in_proximity)
1123
0
        feed_key(ictx, MP_MBTN_LEFT | MP_KEY_STATE_DOWN, 1, false);
1124
1125
0
    notify_tablet_update(ictx);
1126
0
    input_unlock(ictx);
1127
0
}
1128
1129
void mp_input_tablet_tool_up(struct input_ctx *ictx)
1130
0
{
1131
0
    MP_TRACE(ictx, "tablet tool up\n");
1132
1133
0
    input_lock(ictx);
1134
0
    ictx->tablet_tool_down = false;
1135
0
    if (ictx->opts->tablet_emulate_mouse && ictx->tablet_tool_in_proximity)
1136
0
        feed_key(ictx, MP_MBTN_LEFT | MP_KEY_STATE_UP, 1, false);
1137
1138
0
    notify_tablet_update(ictx);
1139
0
    input_unlock(ictx);
1140
0
}
1141
1142
void mp_input_tablet_tool_button(struct input_ctx *ictx, int button, int state)
1143
0
{
1144
0
    if (mp_msg_test(ictx->log, MSGL_TRACE)) {
1145
0
        char *key = mp_input_get_key_name(button);
1146
0
        MP_TRACE(ictx, "tablet tool button %s %s%s \n",
1147
0
                 key,
1148
0
                 (state & MP_KEY_STATE_DOWN) ? "pressed" : "",
1149
0
                 (state & MP_KEY_STATE_UP) ? "released" : "");
1150
0
        talloc_free(key);
1151
0
    }
1152
1153
0
    input_lock(ictx);
1154
1155
0
    switch (button) {
1156
0
    case MP_KEY_TABLET_TOOL_STYLUS_BTN1:
1157
0
        ictx->tablet_tool_stylus_btn1_pressed = state == MP_KEY_STATE_DOWN;
1158
0
        button = MP_MBTN_MID;
1159
0
        break;
1160
0
    case MP_KEY_TABLET_TOOL_STYLUS_BTN2:
1161
0
        ictx->tablet_tool_stylus_btn2_pressed = state == MP_KEY_STATE_DOWN;
1162
0
        button = MP_MBTN_RIGHT;
1163
0
        break;
1164
0
    case MP_KEY_TABLET_TOOL_STYLUS_BTN3:
1165
0
        ictx->tablet_tool_stylus_btn3_pressed = state == MP_KEY_STATE_DOWN;
1166
0
        button = MP_MBTN_BACK;
1167
0
        break;
1168
0
    default:
1169
0
        break;
1170
0
    }
1171
1172
0
    if (ictx->opts->tablet_emulate_mouse && ictx->tablet_tool_in_proximity && button)
1173
0
        feed_key(ictx, button | state, 1, false);
1174
1175
0
    notify_tablet_update(ictx);
1176
0
    input_unlock(ictx);
1177
0
}
1178
1179
void mp_input_set_tablet_pad_focus(struct input_ctx *ictx, bool focus, int buttons)
1180
0
{
1181
0
    MP_TRACE(ictx, "tablet pad focus %s\n", focus ? "enter" : "leave");
1182
1183
0
    input_lock(ictx);
1184
0
    ictx->tablet_pad_focus = focus;
1185
0
    if (!focus) {
1186
0
        for (int i = 0; i < ictx->tablet_pad_buttons; i++)
1187
0
            ictx->tablet_pad_buttons_pressed[i] = false;
1188
0
    }
1189
0
    ictx->tablet_pad_buttons = MPMIN(buttons, MP_MAX_TABLET_PAD_BUTTONS);
1190
0
    notify_tablet_update(ictx);
1191
0
    input_unlock(ictx);
1192
0
}
1193
1194
void mp_input_tablet_pad_button(struct input_ctx *ictx, int button, int state)
1195
0
{
1196
0
    MP_TRACE(ictx, "tablet pad button %d %s%s \n",
1197
0
             button,
1198
0
             (state & MP_KEY_STATE_DOWN) ? "pressed" : "",
1199
0
             (state & MP_KEY_STATE_UP) ? "released" : "");
1200
1201
0
    input_lock(ictx);
1202
0
    if (button < MP_MAX_TABLET_PAD_BUTTONS)
1203
0
        ictx->tablet_pad_buttons_pressed[button] = state == MP_KEY_STATE_DOWN;
1204
0
    else
1205
0
        MP_WARN(ictx, "Tablet pad button %d outside of range!\n", button);
1206
1207
0
    notify_tablet_update(ictx);
1208
0
    input_unlock(ictx);
1209
0
}
1210
1211
void mp_input_set_tablet_pos(struct input_ctx *ictx, int x, int y, bool quiet)
1212
0
{
1213
0
    MP_TRACE(ictx, "tablet tool position %d/%d \n", x, y);
1214
1215
0
    input_lock(ictx);
1216
0
    ictx->tablet_x = x;
1217
0
    ictx->tablet_y = y;
1218
0
    if (ictx->opts->tablet_emulate_mouse && ictx->tablet_tool_in_proximity)
1219
0
        set_mouse_pos(ictx, x, y, quiet);
1220
1221
0
    notify_tablet_update(ictx);
1222
0
    input_unlock(ictx);
1223
0
}
1224
1225
void mp_input_get_tablet_pos(struct input_ctx *ictx, int *x, int *y,
1226
                             bool *tool_in_proximity, bool *tool_down,
1227
                             bool *tool_stylus_btn1_pressed,
1228
                             bool *tool_stylus_btn2_pressed,
1229
                             bool *tool_stylus_btn3_pressed,
1230
                             bool *pad_focus,
1231
                             bool *pad_buttons_pressed,
1232
                             int *pad_buttons)
1233
167
{
1234
167
    input_lock(ictx);
1235
167
    *x = ictx->tablet_x;
1236
167
    *y = ictx->tablet_y;
1237
167
    *tool_in_proximity = ictx->tablet_tool_in_proximity;
1238
167
    *tool_down = ictx->tablet_tool_down;
1239
167
    *tool_stylus_btn1_pressed = ictx->tablet_tool_stylus_btn1_pressed;
1240
167
    *tool_stylus_btn2_pressed = ictx->tablet_tool_stylus_btn2_pressed;
1241
167
    *tool_stylus_btn3_pressed = ictx->tablet_tool_stylus_btn3_pressed;
1242
167
    *pad_focus = ictx->tablet_pad_focus;
1243
167
    for (int i = 0; i < ictx->tablet_pad_buttons; i++)
1244
0
        pad_buttons_pressed[i] = ictx->tablet_pad_buttons_pressed[i];
1245
167
    *pad_buttons = ictx->tablet_pad_buttons;
1246
167
    input_unlock(ictx);
1247
167
}
1248
1249
static bool test_mouse(struct input_ctx *ictx, int x, int y, int rej_flags)
1250
979k
{
1251
979k
    bool res = false;
1252
1.95M
    for (int i = 0; i < ictx->num_active_sections; i++) {
1253
979k
        struct active_section *as = &ictx->active_sections[i];
1254
979k
        if (as->flags & rej_flags)
1255
979k
            continue;
1256
0
        struct cmd_bind_section *s = get_bind_section(ictx, as->name);
1257
0
        if (s->mouse_area_set && test_rect(&s->mouse_area, x, y)) {
1258
0
            res = true;
1259
0
            break;
1260
0
        }
1261
0
    }
1262
979k
    return res;
1263
979k
}
1264
1265
static bool test_mouse_active(struct input_ctx *ictx, int x, int y)
1266
979k
{
1267
979k
    return test_mouse(ictx, x, y, MP_INPUT_ALLOW_HIDE_CURSOR);
1268
979k
}
1269
1270
bool mp_input_test_mouse_active(struct input_ctx *ictx, int x, int y)
1271
0
{
1272
0
    input_lock(ictx);
1273
0
    bool res = test_mouse_active(ictx, x, y);
1274
0
    input_unlock(ictx);
1275
0
    return res;
1276
0
}
1277
1278
bool mp_input_test_dragging(struct input_ctx *ictx, int x, int y)
1279
0
{
1280
0
    input_lock(ictx);
1281
0
    bool r = !ictx->opts->allow_win_drag ||
1282
0
                        test_mouse(ictx, x, y, MP_INPUT_ALLOW_VO_DRAGGING);
1283
0
    input_unlock(ictx);
1284
0
    return r;
1285
0
}
1286
1287
void mp_input_drop_files(struct input_ctx *ictx, int num_files, char **files,
1288
                         enum mp_dnd_action action)
1289
0
{
1290
0
    input_lock(ictx);
1291
0
    TA_FREEP(&ictx->dropped_files);
1292
0
    ictx->dropped_files = talloc_zero_array(ictx, bstr, num_files);
1293
0
    ictx->num_dropped_files = num_files;
1294
0
    ictx->dnd_ts = mp_time_ns();
1295
0
    ictx->dnd_action = action;
1296
0
    for (int i = 0; i < num_files; i++)
1297
0
        ictx->dropped_files[i] = bstrdup(ictx->dropped_files, bstr0(files[i]));
1298
1299
0
    notify_event_update(ictx);
1300
0
    input_unlock(ictx);
1301
0
}
1302
1303
void mp_input_get_dropped_files(struct input_ctx *ictx, void *talloc_ctx,
1304
                                int64_t *dnd_ts,
1305
                                enum mp_dnd_action *dnd_action,
1306
                                char ***dropped_files)
1307
273
{
1308
273
    input_lock(ictx);
1309
273
    *dnd_ts = ictx->dnd_ts;
1310
273
    *dnd_action = ictx->dnd_action;
1311
273
    *dropped_files = NULL;
1312
273
    int num = 0;
1313
273
    for (int i = 0; i < ictx->num_dropped_files; i++)
1314
0
        MP_TARRAY_APPEND(talloc_ctx, *dropped_files, num,
1315
273
                         bstrto0(talloc_ctx, ictx->dropped_files[i]));
1316
273
    MP_TARRAY_APPEND(talloc_ctx, *dropped_files, num, NULL);
1317
273
    input_unlock(ictx);
1318
273
}
1319
1320
unsigned int mp_input_get_mouse_event_counter(struct input_ctx *ictx)
1321
979k
{
1322
    // Make the frontend always display the mouse cursor (as long as it's not
1323
    // forced invisible) if mouse input is desired.
1324
979k
    input_lock(ictx);
1325
979k
    if (test_mouse_active(ictx, ictx->mouse_x, ictx->mouse_y))
1326
0
        ictx->mouse_event_counter++;
1327
979k
    int ret = ictx->mouse_event_counter;
1328
979k
    input_unlock(ictx);
1329
979k
    return ret;
1330
979k
}
1331
1332
// adjust min time to wait until next repeat event
1333
static void adjust_max_wait_time(struct input_ctx *ictx, double *time)
1334
3.05M
{
1335
3.05M
    struct input_opts *opts = ictx->opts;
1336
3.05M
    if (ictx->last_key_down && opts->ar_rate > 0 && ictx->ar_state >= 0) {
1337
0
        *time = MPMIN(*time, 1.0 / opts->ar_rate);
1338
0
        *time = MPMIN(*time, opts->ar_delay / 1000.0);
1339
0
    }
1340
3.05M
}
1341
1342
int mp_input_queue_cmd(struct input_ctx *ictx, mp_cmd_t *cmd)
1343
0
{
1344
0
    if (!cmd)
1345
0
        return 0;
1346
0
    input_lock(ictx);
1347
0
    queue_cmd(ictx, cmd);
1348
0
    input_unlock(ictx);
1349
0
    return 1;
1350
0
}
1351
1352
static mp_cmd_t *check_autorepeat(struct input_ctx *ictx)
1353
3.05M
{
1354
3.05M
    struct input_opts *opts = ictx->opts;
1355
1356
    // No input : autorepeat ?
1357
3.05M
    if (opts->ar_rate <= 0 || !ictx->current_down_cmd || !ictx->last_key_down ||
1358
0
        (ictx->last_key_down & MP_NO_REPEAT_KEY) ||
1359
0
        !mp_input_is_repeatable_cmd(ictx->current_down_cmd))
1360
3.05M
        ictx->ar_state = -1; // disable
1361
1362
3.05M
    if (ictx->ar_state >= 0) {
1363
0
        int64_t t = mp_time_ns();
1364
0
        if (ictx->last_ar + MP_TIME_S_TO_NS(2) < t)
1365
0
            ictx->last_ar = t;
1366
        // First time : wait delay
1367
0
        if (ictx->ar_state == 0
1368
0
            && (t - ictx->last_key_down_time) >= MP_TIME_MS_TO_NS(opts->ar_delay))
1369
0
        {
1370
0
            ictx->ar_state = 1;
1371
0
            ictx->last_ar = ictx->last_key_down_time + MP_TIME_MS_TO_NS(opts->ar_delay);
1372
            // Then send rate / sec event
1373
0
        } else if (ictx->ar_state == 1
1374
0
                   && (t - ictx->last_ar) >= 1e9 / opts->ar_rate) {
1375
0
            ictx->last_ar += 1e9 / opts->ar_rate;
1376
0
        } else {
1377
0
            return NULL;
1378
0
        }
1379
0
        struct mp_cmd *ret = mp_cmd_clone(ictx->current_down_cmd);
1380
0
        ret->repeated = true;
1381
0
        return ret;
1382
0
    }
1383
3.05M
    return NULL;
1384
3.05M
}
1385
1386
double mp_input_get_delay(struct input_ctx *ictx)
1387
3.05M
{
1388
3.05M
    input_lock(ictx);
1389
3.05M
    double seconds = INFINITY;
1390
3.05M
    adjust_max_wait_time(ictx, &seconds);
1391
3.05M
    input_unlock(ictx);
1392
3.05M
    return seconds;
1393
3.05M
}
1394
1395
void mp_input_wakeup(struct input_ctx *ictx)
1396
0
{
1397
0
    ictx->wakeup_cb(ictx->wakeup_ctx);
1398
0
}
1399
1400
mp_cmd_t *mp_input_read_cmd(struct input_ctx *ictx)
1401
3.05M
{
1402
3.05M
    input_lock(ictx);
1403
3.05M
    struct mp_cmd *ret = queue_remove_head(&ictx->cmd_queue);
1404
3.05M
    if (!ret)
1405
3.05M
        ret = check_autorepeat(ictx);
1406
3.05M
    if (ret && ret->mouse_move) {
1407
0
        ictx->mouse_x = ret->mouse_x;
1408
0
        ictx->mouse_y = ret->mouse_y;
1409
0
    }
1410
3.05M
    input_unlock(ictx);
1411
3.05M
    return ret;
1412
3.05M
}
1413
1414
void mp_input_get_mouse_pos(struct input_ctx *ictx, int *x, int *y, int *hover)
1415
318
{
1416
318
    input_lock(ictx);
1417
318
    *x = ictx->mouse_x;
1418
318
    *y = ictx->mouse_y;
1419
318
    *hover = ictx->mouse_hover;
1420
318
    input_unlock(ictx);
1421
318
}
1422
1423
// If name is NULL, return "default".
1424
// Return a statically allocated name of the section (i.e. return value never
1425
// gets deallocated).
1426
static bstr normalize_section(struct input_ctx *ictx, bstr name)
1427
456k
{
1428
456k
    return get_bind_section(ictx, name)->section;
1429
456k
}
1430
1431
static void disable_section(struct input_ctx *ictx, bstr name)
1432
228k
{
1433
228k
    name = normalize_section(ictx, name);
1434
1435
    // Remove old section, or make sure it's on top if re-enabled
1436
228k
    for (int i = ictx->num_active_sections - 1; i >= 0; i--) {
1437
392
        struct active_section *as = &ictx->active_sections[i];
1438
392
        if (bstr_equals(as->name, name)) {
1439
0
            MP_TARRAY_REMOVE_AT(ictx->active_sections,
1440
0
                                ictx->num_active_sections, i);
1441
0
        }
1442
392
    }
1443
228k
}
1444
1445
void mp_input_disable_section(struct input_ctx *ictx, char *name)
1446
0
{
1447
0
    input_lock(ictx);
1448
0
    disable_section(ictx, bstr0(name));
1449
0
    input_unlock(ictx);
1450
0
}
1451
1452
void mp_input_enable_section(struct input_ctx *ictx, char *name, int flags)
1453
228k
{
1454
228k
    bstr bname = bstr0(name);
1455
228k
    input_lock(ictx);
1456
228k
    bname = normalize_section(ictx, bname);
1457
1458
228k
    disable_section(ictx, bname);
1459
1460
228k
    MP_TRACE(ictx, "enable section '%.*s'\n", BSTR_P(bname));
1461
1462
228k
    int top = ictx->num_active_sections;
1463
228k
    if (!(flags & MP_INPUT_ON_TOP)) {
1464
        // insert before the first top entry
1465
228k
        for (top = 0; top < ictx->num_active_sections; top++) {
1466
392
            if (ictx->active_sections[top].flags & MP_INPUT_ON_TOP)
1467
0
                break;
1468
392
        }
1469
228k
    }
1470
228k
    MP_TARRAY_INSERT_AT(ictx, ictx->active_sections, ictx->num_active_sections,
1471
228k
                        top, (struct active_section){bname, flags});
1472
1473
228k
    MP_TRACE(ictx, "active section stack:\n");
1474
456k
    for (int n = 0; n < ictx->num_active_sections; n++) {
1475
228k
        MP_TRACE(ictx, " %.*s %d\n", BSTR_P(ictx->active_sections[n].name),
1476
228k
                 ictx->active_sections[n].flags);
1477
228k
    }
1478
1479
228k
    input_unlock(ictx);
1480
228k
}
1481
1482
void mp_input_disable_all_sections(struct input_ctx *ictx)
1483
0
{
1484
0
    input_lock(ictx);
1485
0
    ictx->num_active_sections = 0;
1486
0
    input_unlock(ictx);
1487
0
}
1488
1489
void mp_input_set_section_mouse_area(struct input_ctx *ictx, char *name,
1490
                                     int x0, int y0, int x1, int y1)
1491
0
{
1492
0
    input_lock(ictx);
1493
0
    struct cmd_bind_section *s = get_bind_section(ictx, bstr0(name));
1494
0
    s->mouse_area = (struct mp_rect){x0, y0, x1, y1};
1495
0
    s->mouse_area_set = x0 != x1 && y0 != y1;
1496
0
    input_unlock(ictx);
1497
0
}
1498
1499
static void bind_dealloc(struct cmd_bind *bind)
1500
132k
{
1501
132k
    talloc_free(bind->cmd);
1502
132k
    talloc_free(bind->location);
1503
132k
    talloc_free(bind->desc);
1504
132k
}
1505
1506
// builtin: if true, remove all builtin binds, else remove all user binds
1507
static void remove_binds(struct cmd_bind_section *bs, bool builtin)
1508
0
{
1509
0
    for (int n = bs->num_binds - 1; n >= 0; n--) {
1510
0
        if (bs->binds[n].is_builtin == builtin) {
1511
0
            bind_dealloc(&bs->binds[n]);
1512
0
            mp_assert(bs->num_binds >= 1);
1513
0
            bs->binds[n] = bs->binds[bs->num_binds - 1];
1514
0
            bs->num_binds--;
1515
0
        }
1516
0
    }
1517
0
}
1518
1519
void mp_input_define_section(struct input_ctx *ictx, char *name, char *location,
1520
                             char *contents, bool builtin, char *owner)
1521
0
{
1522
0
    bstr bname = bstr0(name);
1523
0
    if (!bname.len)
1524
0
        return; // parse_config() changes semantics with restrict_section==empty
1525
0
    input_lock(ictx);
1526
    // Delete:
1527
0
    struct cmd_bind_section *bs = get_bind_section(ictx, bname);
1528
0
    if ((!bs->owner || (owner && strcmp(bs->owner, owner) != 0)) &&
1529
0
        !bstr_equals0(bs->section, "default"))
1530
0
    {
1531
0
        talloc_replace(bs, bs->owner, owner);
1532
0
    }
1533
0
    remove_binds(bs, builtin);
1534
0
    if (contents && contents[0]) {
1535
        // Redefine:
1536
0
        parse_config(ictx, builtin, bstr0(contents), location, bname);
1537
0
    } else {
1538
        // Disable:
1539
0
        disable_section(ictx, bname);
1540
0
    }
1541
0
    input_unlock(ictx);
1542
0
}
1543
1544
void mp_input_remove_sections_by_owner(struct input_ctx *ictx, char *owner)
1545
438k
{
1546
438k
    input_lock(ictx);
1547
881k
    for (int n = 0; n < ictx->num_sections; n++) {
1548
442k
        struct cmd_bind_section *bs = ictx->sections[n];
1549
442k
        if (bs->owner && owner && strcmp(bs->owner, owner) == 0) {
1550
0
            disable_section(ictx, bs->section);
1551
0
            remove_binds(bs, false);
1552
0
            remove_binds(bs, true);
1553
0
        }
1554
442k
    }
1555
438k
    input_unlock(ictx);
1556
438k
}
1557
1558
static bool bind_matches_key(struct cmd_bind *bind, int num_keys, const int *keys)
1559
605k
{
1560
605k
    if (bind->num_keys != num_keys)
1561
44.8k
        return false;
1562
703k
    for (int i = 0; i < num_keys; i++) {
1563
574k
        if (bind->keys[i] != keys[i])
1564
431k
            return false;
1565
574k
    }
1566
128k
    return true;
1567
560k
}
1568
1569
static void bind_keys(struct input_ctx *ictx, bool builtin, bstr section,
1570
                      const int *keys, int num_keys, bstr command,
1571
                      const char *loc, const char *desc)
1572
132k
{
1573
132k
    struct cmd_bind_section *bs = get_bind_section(ictx, section);
1574
132k
    struct cmd_bind *bind = NULL;
1575
1576
132k
    mp_assert(num_keys <= MP_MAX_KEY_DOWN);
1577
1578
620k
    for (int n = 0; n < bs->num_binds; n++) {
1579
605k
        struct cmd_bind *b = &bs->binds[n];
1580
605k
        if (bind_matches_key(b, num_keys, keys) && b->is_builtin == builtin) {
1581
117k
            bind = b;
1582
117k
            break;
1583
117k
        }
1584
605k
    }
1585
1586
132k
    if (!bind) {
1587
15.1k
        struct cmd_bind empty = {{0}};
1588
15.1k
        MP_TARRAY_APPEND(bs, bs->binds, bs->num_binds, empty);
1589
15.1k
        bind = &bs->binds[bs->num_binds - 1];
1590
15.1k
    }
1591
1592
132k
    bind_dealloc(bind);
1593
1594
132k
    *bind = (struct cmd_bind) {
1595
132k
        .cmd = bstrdup0(bs->binds, command),
1596
132k
        .location = talloc_strdup(bs->binds, loc),
1597
132k
        .desc = talloc_strdup(bs->binds, desc),
1598
132k
        .owner = bs,
1599
132k
        .is_builtin = builtin,
1600
132k
        .num_keys = num_keys,
1601
132k
    };
1602
132k
    memcpy(bind->keys, keys, num_keys * sizeof(bind->keys[0]));
1603
132k
    if (mp_msg_test(ictx->log, MSGL_DEBUG)) {
1604
132k
        char *s = mp_input_get_key_combo_name(keys, num_keys);
1605
132k
        MP_TRACE(ictx, "add: section='%.*s' key='%s'%s cmd='%s' location='%s'\n",
1606
132k
                 BSTR_P(bind->owner->section), s, bind->is_builtin ? " builtin" : "",
1607
132k
                 bind->cmd, bind->location);
1608
132k
        talloc_free(s);
1609
132k
    }
1610
132k
}
1611
1612
// restrict_section: every entry is forced to this section name
1613
//          if NULL, load normally and allow any sections
1614
static int parse_config(struct input_ctx *ictx, bool builtin, bstr data,
1615
                        const char *location, const bstr restrict_section)
1616
8.13k
{
1617
8.13k
    int n_binds = 0;
1618
8.13k
    int line_no = 0;
1619
8.13k
    char *cur_loc = NULL;
1620
1621
179k
    while (data.len) {
1622
171k
        line_no++;
1623
171k
        if (cur_loc)
1624
163k
            talloc_free(cur_loc);
1625
171k
        cur_loc = talloc_asprintf(NULL, "%s:%d", location, line_no);
1626
1627
171k
        bstr line = bstr_strip_linebreaks(bstr_getline(data, &data));
1628
171k
        line = bstr_lstrip(line);
1629
171k
        if (line.len == 0 || bstr_startswith0(line, "#"))
1630
14.3k
            continue;
1631
157k
        if (bstr_eatstart0(&line, "default-bindings ")) {
1632
4.61k
            bstr orig = line;
1633
4.61k
            bstr_split_tok(line, "#", &line, &(bstr){0});
1634
4.61k
            line = bstr_strip(line);
1635
4.61k
            if (bstr_equals0(line, "start")) {
1636
2.60k
                builtin = true;
1637
2.60k
            } else {
1638
2.01k
                MP_ERR(ictx, "Broken line: %.*s at %s\n", BSTR_P(orig), cur_loc);
1639
2.01k
            }
1640
4.61k
            continue;
1641
4.61k
        }
1642
152k
        struct bstr command;
1643
        // Find the key name starting a line
1644
152k
        struct bstr keyname = bstr_split(line, WHITESPACE, &command);
1645
152k
        command = bstr_strip(command);
1646
152k
        if (command.len == 0) {
1647
9.16k
            MP_ERR(ictx, "Unfinished key binding: %.*s at %s\n", BSTR_P(line),
1648
9.16k
                   cur_loc);
1649
9.16k
            continue;
1650
9.16k
        }
1651
143k
        char *name = bstrdup0(NULL, keyname);
1652
143k
        int keys[MP_MAX_KEY_DOWN];
1653
143k
        int num_keys = 0;
1654
143k
        if (!mp_input_get_keys_from_string(name, MP_MAX_KEY_DOWN, &num_keys, keys)) {
1655
10.7k
            talloc_free(name);
1656
10.7k
            MP_ERR(ictx, "Unknown key '%.*s' at %s\n", BSTR_P(keyname), cur_loc);
1657
10.7k
            continue;
1658
10.7k
        }
1659
132k
        talloc_free(name);
1660
1661
132k
        bstr section = restrict_section;
1662
132k
        if (!section.len) {
1663
132k
            if (bstr_startswith0(command, "{")) {
1664
10.7k
                int p = bstrchr(command, '}');
1665
10.7k
                if (p != -1) {
1666
7.65k
                    section = bstr_strip(bstr_splice(command, 1, p));
1667
7.65k
                    command = bstr_lstrip(bstr_cut(command, p + 1));
1668
7.65k
                }
1669
10.7k
            }
1670
132k
        }
1671
1672
        // Print warnings if invalid commands are encountered.
1673
132k
        struct mp_cmd *cmd = mp_input_parse_cmd(ictx, command, cur_loc);
1674
132k
        const char *desc = NULL;
1675
132k
        if (cmd) {
1676
22.6k
            desc = cmd->desc;
1677
22.6k
            command = bstr0(cmd->original);
1678
22.6k
        }
1679
1680
132k
        bind_keys(ictx, builtin, section, keys, num_keys, command, cur_loc, desc);
1681
132k
        n_binds++;
1682
1683
132k
        talloc_free(cmd);
1684
132k
    }
1685
1686
8.13k
    talloc_free(cur_loc);
1687
1688
8.13k
    return n_binds;
1689
8.13k
}
1690
1691
static bool parse_config_file(struct input_ctx *ictx, char *file)
1692
13.9k
{
1693
13.9k
    bool r = false;
1694
13.9k
    void *tmp = talloc_new(NULL);
1695
1696
13.9k
    file = mp_get_user_path(tmp, ictx->global, file);
1697
1698
13.9k
    bstr data = stream_read_file2(file, tmp, STREAM_ORIGIN_DIRECT | STREAM_READ,
1699
13.9k
                                  ictx->global, 1000000);
1700
13.9k
    if (data.start) {
1701
7.42k
        MP_VERBOSE(ictx, "Parsing input config file %s\n", file);
1702
7.42k
        bstr_eatstart0(&data, "\xEF\xBB\xBF"); // skip BOM
1703
7.42k
        int num = parse_config(ictx, false, data, file, (bstr){0});
1704
7.42k
        MP_VERBOSE(ictx, "Input config file %s parsed: %d binds\n", file, num);
1705
7.42k
        r = true;
1706
7.42k
    } else {
1707
6.56k
        MP_ERR(ictx, "Error reading input config file %s\n", file);
1708
6.56k
    }
1709
1710
13.9k
    talloc_free(tmp);
1711
13.9k
    return r;
1712
13.9k
}
1713
1714
struct input_ctx *mp_input_init(struct mpv_global *global,
1715
                                void (*wakeup_cb)(void *ctx),
1716
                                void *wakeup_ctx)
1717
227k
{
1718
1719
227k
    struct input_ctx *ictx = talloc_ptrtype(NULL, ictx);
1720
227k
    *ictx = (struct input_ctx){
1721
227k
        .global = global,
1722
227k
        .ar_state = -1,
1723
227k
        .log = mp_log_new(ictx, global->log, "input"),
1724
227k
        .mouse_section = bstr0("default"),
1725
227k
        .opts_cache = m_config_cache_alloc(ictx, global, &input_config),
1726
227k
        .wakeup_cb = wakeup_cb,
1727
227k
        .wakeup_ctx = wakeup_ctx,
1728
227k
        .active_sections = talloc_array(ictx, struct active_section, 0),
1729
227k
        .touch_points = talloc_array(ictx, struct touch_point, 0),
1730
227k
        .dnd_action = DND_NONE,
1731
227k
    };
1732
1733
227k
    ictx->opts = ictx->opts_cache->opts;
1734
1735
227k
    mp_mutex_init(&ictx->mutex);
1736
1737
    // Setup default section, so that it does nothing.
1738
227k
    mp_input_enable_section(ictx, NULL, MP_INPUT_ALLOW_VO_DRAGGING |
1739
227k
                                        MP_INPUT_ALLOW_HIDE_CURSOR);
1740
1741
227k
    return ictx;
1742
227k
}
1743
1744
static void reload_opts(struct input_ctx *ictx, bool shutdown)
1745
653k
{
1746
653k
    m_config_cache_update(ictx->opts_cache);
1747
1748
#if HAVE_COCOA
1749
    struct input_opts *opts = ictx->opts;
1750
1751
    if (ictx->using_cocoa_media_keys != (opts->use_media_keys && !shutdown)) {
1752
        ictx->using_cocoa_media_keys = !ictx->using_cocoa_media_keys;
1753
        if (ictx->using_cocoa_media_keys) {
1754
            cocoa_init_media_keys();
1755
        } else {
1756
            cocoa_uninit_media_keys();
1757
        }
1758
    }
1759
#endif
1760
653k
}
1761
1762
void mp_input_update_opts(struct input_ctx *ictx)
1763
212k
{
1764
212k
    input_lock(ictx);
1765
212k
    reload_opts(ictx, false);
1766
212k
    input_unlock(ictx);
1767
212k
}
1768
1769
void mp_input_load_config(struct input_ctx *ictx)
1770
212k
{
1771
212k
    input_lock(ictx);
1772
1773
212k
    reload_opts(ictx, false);
1774
1775
    // "Uncomment" the default key bindings in etc/input.conf and add them.
1776
    // All lines that do not start with '# ' are parsed.
1777
212k
    bstr builtin = bstr0(builtin_input_conf);
1778
213k
    while (ictx->opts->builtin_bindings && builtin.len) {
1779
795
        bstr line = bstr_getline(builtin, &builtin);
1780
795
        bstr_eatstart0(&line, "#");
1781
795
        if (!bstr_startswith0(line, " "))
1782
702
            parse_config(ictx, true, line, "<builtin>", (bstr){0});
1783
795
    }
1784
1785
212k
    bool config_ok = false;
1786
212k
    if (ictx->opts->config_file && ictx->opts->config_file[0])
1787
7.57k
        config_ok = parse_config_file(ictx, ictx->opts->config_file);
1788
212k
    if (!config_ok) {
1789
        // Try global conf dir
1790
211k
        void *tmp = talloc_new(NULL);
1791
211k
        char **files = mp_find_all_config_files(tmp, ictx->global, "input.conf");
1792
211k
        for (int n = 0; files && files[n]; n++)
1793
0
            parse_config_file(ictx, files[n]);
1794
211k
        talloc_free(tmp);
1795
211k
    }
1796
1797
212k
    bool use_gamepad = ictx->opts->use_gamepad;
1798
212k
    input_unlock(ictx);
1799
1800
#if HAVE_SDL2_GAMEPAD
1801
    if (use_gamepad)
1802
        mp_input_sdl_gamepad_add(ictx);
1803
#else
1804
212k
    (void)use_gamepad;
1805
212k
#endif
1806
212k
}
1807
1808
bool mp_input_load_config_file(struct input_ctx *ictx, char *file)
1809
6.42k
{
1810
6.42k
    input_lock(ictx);
1811
6.42k
    bool result = parse_config_file(ictx, file);
1812
6.42k
    input_unlock(ictx);
1813
6.42k
    return result;
1814
6.42k
}
1815
1816
static void clear_queue(struct cmd_queue *queue)
1817
227k
{
1818
227k
    while (queue->first) {
1819
0
        struct mp_cmd *item = queue->first;
1820
0
        queue_remove(queue, item);
1821
0
        talloc_free(item);
1822
0
    }
1823
227k
}
1824
1825
void mp_input_uninit(struct input_ctx *ictx)
1826
227k
{
1827
227k
    if (!ictx)
1828
0
        return;
1829
1830
227k
    input_lock(ictx);
1831
227k
    reload_opts(ictx, true);
1832
227k
    input_unlock(ictx);
1833
1834
227k
    close_input_sources(ictx);
1835
227k
    clear_queue(&ictx->cmd_queue);
1836
227k
    talloc_free(ictx->current_down_cmd);
1837
227k
    mp_mutex_destroy(&ictx->mutex);
1838
227k
    talloc_free(ictx);
1839
227k
}
1840
1841
bool mp_input_use_alt_gr(struct input_ctx *ictx)
1842
0
{
1843
0
    input_lock(ictx);
1844
0
    bool r = ictx->opts->use_alt_gr;
1845
0
    input_unlock(ictx);
1846
0
    return r;
1847
0
}
1848
1849
bool mp_input_use_media_keys(struct input_ctx *ictx)
1850
0
{
1851
0
    input_lock(ictx);
1852
0
    bool r = ictx->opts->use_media_keys;
1853
0
    input_unlock(ictx);
1854
0
    return r;
1855
0
}
1856
1857
struct mp_cmd *mp_input_parse_cmd(struct input_ctx *ictx, bstr str,
1858
                                  const char *location)
1859
132k
{
1860
132k
    return mp_input_parse_cmd_str(ictx->log, str, location);
1861
132k
}
1862
1863
void mp_input_run_cmd(struct input_ctx *ictx, const char **cmd)
1864
0
{
1865
0
    input_lock(ictx);
1866
0
    queue_cmd(ictx, mp_input_parse_cmd_strv(ictx->log, cmd));
1867
0
    input_unlock(ictx);
1868
0
}
1869
1870
bool mp_input_bind_key(struct input_ctx *ictx, const char *key, bstr command,
1871
                       const char *desc)
1872
0
{
1873
0
    char *name = talloc_strdup(NULL, key);
1874
0
    int keys[MP_MAX_KEY_DOWN];
1875
0
    int num_keys = 0;
1876
0
    if (!mp_input_get_keys_from_string(name, MP_MAX_KEY_DOWN, &num_keys, keys)) {
1877
0
        talloc_free(name);
1878
0
        return false;
1879
0
    }
1880
0
    talloc_free(name);
1881
1882
0
    input_lock(ictx);
1883
0
    bind_keys(ictx, false, (bstr){0}, keys, num_keys, command,
1884
0
              "keybind-command", desc);
1885
0
    input_unlock(ictx);
1886
0
    return true;
1887
0
}
1888
1889
struct mpv_node mp_input_get_bindings(struct input_ctx *ictx)
1890
196
{
1891
196
    input_lock(ictx);
1892
196
    struct mpv_node root;
1893
196
    node_init(&root, MPV_FORMAT_NODE_ARRAY, NULL);
1894
1895
395
    for (int x = 0; x < ictx->num_sections; x++) {
1896
199
        struct cmd_bind_section *s = ictx->sections[x];
1897
199
        int priority = -1;
1898
1899
202
        for (int i = 0; i < ictx->num_active_sections; i++) {
1900
202
            struct active_section *as = &ictx->active_sections[i];
1901
202
            if (bstr_equals(as->name, s->section)) {
1902
199
                priority = i;
1903
199
                break;
1904
199
            }
1905
202
        }
1906
1907
199
        for (int n = 0; n < s->num_binds; n++) {
1908
0
            struct cmd_bind *b = &s->binds[n];
1909
0
            struct mpv_node *entry = node_array_add(&root, MPV_FORMAT_NODE_MAP);
1910
1911
0
            int b_priority = priority;
1912
0
            if (b->is_builtin && !ictx->opts->default_bindings)
1913
0
                b_priority = -1;
1914
1915
            // Try to fixup the weird logic so consumer of this bindings list
1916
            // does not get too confused.
1917
0
            if (b_priority >= 0 && !b->is_builtin)
1918
0
                b_priority += ictx->num_active_sections;
1919
1920
0
            node_map_add_bstr(entry, "section", s->section);
1921
0
            if (s->owner)
1922
0
                node_map_add_string(entry, "owner", s->owner);
1923
0
            node_map_add_string(entry, "cmd", b->cmd);
1924
0
            node_map_add_flag(entry, "is_weak", b->is_builtin);
1925
0
            node_map_add_int64(entry, "priority", b_priority);
1926
0
            if (b->desc)
1927
0
                node_map_add_string(entry, "comment", b->desc);
1928
1929
0
            char *key = mp_input_get_key_combo_name(b->keys, b->num_keys);
1930
0
            node_map_add_string(entry, "key", key);
1931
0
            talloc_free(key);
1932
0
        }
1933
199
    }
1934
1935
196
    input_unlock(ictx);
1936
196
    return root;
1937
196
}
1938
1939
struct mp_input_src_internal {
1940
    mp_thread thread;
1941
    bool thread_running;
1942
    bool init_done;
1943
1944
    char *cmd_buffer;
1945
    size_t cmd_buffer_size;
1946
    bool drop;
1947
};
1948
1949
static struct mp_input_src *input_add_src(struct input_ctx *ictx)
1950
0
{
1951
0
    input_lock(ictx);
1952
0
    if (ictx->num_sources == MP_MAX_SOURCES) {
1953
0
        input_unlock(ictx);
1954
0
        return NULL;
1955
0
    }
1956
1957
0
    char name[80];
1958
0
    snprintf(name, sizeof(name), "#%d", ictx->num_sources + 1);
1959
0
    struct mp_input_src *src = talloc_ptrtype(NULL, src);
1960
0
    *src = (struct mp_input_src){
1961
0
        .global = ictx->global,
1962
0
        .log = mp_log_new(src, ictx->log, name),
1963
0
        .input_ctx = ictx,
1964
0
        .in = talloc_zero(src, struct mp_input_src_internal),
1965
0
    };
1966
1967
0
    ictx->sources[ictx->num_sources++] = src;
1968
1969
0
    input_unlock(ictx);
1970
0
    return src;
1971
0
}
1972
1973
static void input_src_kill(struct mp_input_src *src);
1974
1975
static void close_input_sources(struct input_ctx *ictx)
1976
227k
{
1977
    // To avoid lock-order issues, we first remove each source from the context,
1978
    // and then destroy it.
1979
227k
    while (1) {
1980
227k
        input_lock(ictx);
1981
227k
        struct mp_input_src *src = ictx->num_sources ? ictx->sources[0] : NULL;
1982
227k
        input_unlock(ictx);
1983
227k
        if (!src)
1984
227k
            break;
1985
0
        input_src_kill(src);
1986
0
    }
1987
227k
}
1988
1989
static void input_src_kill(struct mp_input_src *src)
1990
0
{
1991
0
    if (!src)
1992
0
        return;
1993
0
    struct input_ctx *ictx = src->input_ctx;
1994
0
    input_lock(ictx);
1995
0
    for (int n = 0; n < ictx->num_sources; n++) {
1996
0
        if (ictx->sources[n] == src) {
1997
0
            MP_TARRAY_REMOVE_AT(ictx->sources, ictx->num_sources, n);
1998
0
            input_unlock(ictx);
1999
0
            if (src->cancel)
2000
0
                src->cancel(src);
2001
0
            if (src->in->thread_running)
2002
0
                mp_thread_join(src->in->thread);
2003
0
            if (src->uninit)
2004
0
                src->uninit(src);
2005
0
            talloc_free(src);
2006
0
            return;
2007
0
        }
2008
0
    }
2009
0
    MP_ASSERT_UNREACHABLE();
2010
0
}
2011
2012
void mp_input_src_init_done(struct mp_input_src *src)
2013
0
{
2014
0
    mp_assert(!src->in->init_done);
2015
0
    mp_assert(src->in->thread_running);
2016
0
    mp_assert(mp_thread_id_equal(mp_thread_get_id(src->in->thread), mp_thread_current_id()));
2017
0
    src->in->init_done = true;
2018
0
    mp_rendezvous(&src->in->init_done, 0);
2019
0
}
2020
2021
static MP_THREAD_VOID input_src_thread(void *ptr)
2022
0
{
2023
0
    void **args = ptr;
2024
0
    struct mp_input_src *src = args[0];
2025
0
    void (*loop_fn)(struct mp_input_src *src, void *ctx) = args[1];
2026
0
    void *ctx = args[2];
2027
2028
0
    mp_thread_set_name("input");
2029
2030
0
    src->in->thread_running = true;
2031
2032
0
    loop_fn(src, ctx);
2033
2034
0
    if (!src->in->init_done)
2035
0
        mp_rendezvous(&src->in->init_done, -1);
2036
2037
0
    MP_THREAD_RETURN();
2038
0
}
2039
2040
int mp_input_add_thread_src(struct input_ctx *ictx, void *ctx,
2041
    void (*loop_fn)(struct mp_input_src *src, void *ctx))
2042
0
{
2043
0
    struct mp_input_src *src = input_add_src(ictx);
2044
0
    if (!src)
2045
0
        return -1;
2046
2047
0
    void *args[] = {src, loop_fn, ctx};
2048
0
    if (mp_thread_create(&src->in->thread, input_src_thread, args)) {
2049
0
        input_src_kill(src);
2050
0
        return -1;
2051
0
    }
2052
0
    if (mp_rendezvous(&src->in->init_done, 0) < 0) {
2053
0
        input_src_kill(src);
2054
0
        return -1;
2055
0
    }
2056
0
    return 0;
2057
0
}
2058
2059
void mp_input_set_repeat_info(struct input_ctx *ictx, int rate, int delay)
2060
0
{
2061
0
    input_lock(ictx);
2062
0
    ictx->opts->ar_rate = rate;
2063
0
    ictx->opts->ar_delay = delay;
2064
0
    input_unlock(ictx);
2065
0
}