Coverage Report

Created: 2025-11-24 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/src/misc/actions.c
Line
Count
Source
1
/*****************************************************************************
2
 * actions.c: handle vlc actions
3
 *****************************************************************************
4
 * Copyright (C) 2003-2016 VLC authors and VideoLAN
5
 *
6
 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program; if not, write to the Free Software Foundation,
20
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22
23
#ifdef HAVE_CONFIG_H
24
# include <config.h>
25
#endif
26
27
/**
28
 * \file
29
 * This file defines functions and structures for hotkey handling in vlc
30
 */
31
32
#include <assert.h>
33
#include <stdlib.h>
34
#include <limits.h>
35
#ifdef HAVE_SEARCH_H
36
# include <search.h>
37
#endif
38
#include <errno.h>
39
40
#include <vlc_common.h>
41
#include <vlc_actions.h>
42
#include <vlc_charset.h>
43
#include "../libvlc.h"
44
45
static const struct key_descriptor
46
{
47
    const char psz[20];
48
    uint32_t i_code;
49
} s_keys[] =
50
{   /* Alphabetical order */
51
    { N_("Backspace"),         KEY_BACKSPACE         },
52
    { N_("Brightness Down"),   KEY_BRIGHTNESS_DOWN   },
53
    { N_("Brightness Up"),     KEY_BRIGHTNESS_UP     },
54
    { N_("Browser Back"),      KEY_BROWSER_BACK      },
55
    { N_("Browser Favorites"), KEY_BROWSER_FAVORITES },
56
    { N_("Browser Forward"),   KEY_BROWSER_FORWARD   },
57
    { N_("Browser Home"),      KEY_BROWSER_HOME      },
58
    { N_("Browser Refresh"),   KEY_BROWSER_REFRESH   },
59
    { N_("Browser Search"),    KEY_BROWSER_SEARCH    },
60
    { N_("Browser Stop"),      KEY_BROWSER_STOP      },
61
    { N_("Delete"),            KEY_DELETE            },
62
    { N_("Down"),              KEY_DOWN              },
63
    { N_("End"),               KEY_END               },
64
    { N_("Enter"),             KEY_ENTER             },
65
    { N_("Esc"),               KEY_ESC               },
66
    { N_("F1"),                KEY_F1                },
67
    { N_("F10"),               KEY_F10               },
68
    { N_("F11"),               KEY_F11               },
69
    { N_("F12"),               KEY_F12               },
70
    { N_("F13"),               KEY_F(13)             },
71
    { N_("F14"),               KEY_F(14)             },
72
    { N_("F15"),               KEY_F(15)             },
73
    { N_("F16"),               KEY_F(16)             },
74
    { N_("F17"),               KEY_F(17)             },
75
    { N_("F18"),               KEY_F(18)             },
76
    { N_("F19"),               KEY_F(19)             },
77
    { N_("F2"),                KEY_F2                },
78
    { N_("F20"),               KEY_F(20)             },
79
    { N_("F21"),               KEY_F(21)             },
80
    { N_("F22"),               KEY_F(22)             },
81
    { N_("F23"),               KEY_F(23)             },
82
    { N_("F24"),               KEY_F(24)             },
83
    { N_("F25"),               KEY_F(25)             },
84
    { N_("F26"),               KEY_F(26)             },
85
    { N_("F27"),               KEY_F(27)             },
86
    { N_("F28"),               KEY_F(28)             },
87
    { N_("F29"),               KEY_F(29)             },
88
    { N_("F3"),                KEY_F3                },
89
    { N_("F30"),               KEY_F(30)             },
90
    { N_("F31"),               KEY_F(31)             },
91
    { N_("F32"),               KEY_F(32)             },
92
    { N_("F33"),               KEY_F(33)             },
93
    { N_("F34"),               KEY_F(34)             },
94
    { N_("F35"),               KEY_F(35)             },
95
    { N_("F4"),                KEY_F4                },
96
    { N_("F5"),                KEY_F5                },
97
    { N_("F6"),                KEY_F6                },
98
    { N_("F7"),                KEY_F7                },
99
    { N_("F8"),                KEY_F8                },
100
    { N_("F9"),                KEY_F9                },
101
    { N_("Home"),              KEY_HOME              },
102
    { N_("Insert"),            KEY_INSERT            },
103
    { N_("Left"),              KEY_LEFT              },
104
    { N_("Media Angle"),       KEY_MEDIA_ANGLE       },
105
    { N_("Media Audio Track"), KEY_MEDIA_AUDIO       },
106
    { N_("Media Forward"),     KEY_MEDIA_FORWARD     },
107
    { N_("Media Menu"),        KEY_MEDIA_MENU        },
108
    { N_("Media Next Frame"),  KEY_MEDIA_FRAME_NEXT  },
109
    { N_("Media Next Track"),  KEY_MEDIA_NEXT_TRACK  },
110
    { N_("Media Play Pause"),  KEY_MEDIA_PLAY_PAUSE  },
111
    { N_("Media Prev Frame"),  KEY_MEDIA_FRAME_PREV  },
112
    { N_("Media Prev Track"),  KEY_MEDIA_PREV_TRACK  },
113
    { N_("Media Record"),      KEY_MEDIA_RECORD      },
114
    { N_("Media Repeat"),      KEY_MEDIA_REPEAT      },
115
    { N_("Media Rewind"),      KEY_MEDIA_REWIND      },
116
    { N_("Media Select"),      KEY_MEDIA_SELECT      },
117
    { N_("Media Shuffle"),     KEY_MEDIA_SHUFFLE     },
118
    { N_("Media Stop"),        KEY_MEDIA_STOP        },
119
    { N_("Media Subtitle"),    KEY_MEDIA_SUBTITLE    },
120
    { N_("Media Time"),        KEY_MEDIA_TIME        },
121
    { N_("Media View"),        KEY_MEDIA_VIEW        },
122
    { N_("Menu"),              KEY_MENU              },
123
    { N_("Mouse Wheel Down"),  KEY_MOUSEWHEELDOWN    },
124
    { N_("Mouse Wheel Left"),  KEY_MOUSEWHEELLEFT    },
125
    { N_("Mouse Wheel Right"), KEY_MOUSEWHEELRIGHT   },
126
    { N_("Mouse Wheel Up"),    KEY_MOUSEWHEELUP      },
127
    { N_("Page Down"),         KEY_PAGEDOWN          },
128
    { N_("Page Up"),           KEY_PAGEUP            },
129
    { N_("Pause"),             KEY_PAUSE             },
130
    { N_("Print"),             KEY_PRINT             },
131
    { N_("Right"),             KEY_RIGHT             },
132
    { N_("Space"),             ' '                   },
133
    { N_("Tab"),               KEY_TAB               },
134
    { N_("Unset"),             KEY_UNSET             },
135
    { N_("Up"),                KEY_UP                },
136
    { N_("Volume Down"),       KEY_VOLUME_DOWN       },
137
    { N_("Volume Mute"),       KEY_VOLUME_MUTE       },
138
    { N_("Volume Up"),         KEY_VOLUME_UP         },
139
    { N_("Zoom In"),           KEY_ZOOM_IN           },
140
    { N_("Zoom Out"),          KEY_ZOOM_OUT          },
141
};
142
143
static int keystrcmp (const void *key, const void *elem)
144
37.6k
{
145
37.6k
    const char *sa = key, *sb = elem;
146
37.6k
    return strcmp (sa, sb);
147
37.6k
}
148
149
/* Convert Unicode code point to UTF-8 */
150
static char *utf8_cp (uint_fast32_t cp, char *buf)
151
0
{
152
0
    if (cp < (1 << 7))
153
0
    {
154
0
        buf[1] = 0;
155
0
        buf[0] = cp;
156
0
    }
157
0
    else if (cp < (1 << 11))
158
0
    {
159
0
        buf[2] = 0;
160
0
        buf[1] = 0x80 | (cp & 0x3F);
161
0
        cp >>= 6;
162
0
        buf[0] = 0xC0 | cp;
163
0
    }
164
0
    else if (cp < (1 << 16))
165
0
    {
166
0
        buf[3] = 0;
167
0
        buf[2] = 0x80 | (cp & 0x3F);
168
0
        cp >>= 6;
169
0
        buf[1] = 0x80 | (cp & 0x3F);
170
0
        cp >>= 6;
171
0
        buf[0] = 0xE0 | cp;
172
0
    }
173
0
    else if (cp < (1 << 21))
174
0
    {
175
0
        buf[4] = 0;
176
0
        buf[3] = 0x80 | (cp & 0x3F);
177
0
        cp >>= 6;
178
0
        buf[2] = 0x80 | (cp & 0x3F);
179
0
        cp >>= 6;
180
0
        buf[1] = 0x80 | (cp & 0x3F);
181
0
        cp >>= 6;
182
0
        buf[0] = 0xE0 | cp;
183
0
    }
184
0
    else
185
0
        return NULL;
186
0
    return buf;
187
0
}
188
189
/**
190
 * Parse a human-readable string representation of a VLC key code.
191
 * @note This only works with the American English representation
192
 * (a.k.a. C or POSIX), not with the local representation returned from
193
 * vlc_keycode2str().
194
 * @return a VLC key code, or KEY_UNSET on failure.
195
 */
196
uint_fast32_t vlc_str2keycode (const char *name)
197
6.21k
{
198
6.21k
    uint_fast32_t mods = 0;
199
6.21k
    uint32_t code;
200
201
6.21k
    for (;;)
202
9.77k
    {
203
9.77k
        size_t len = strcspn (name, "-+");
204
9.77k
        if (len == 0 || name[len] == '\0')
205
6.21k
            break;
206
207
3.56k
        if (len == 4 && !strncasecmp (name, "Ctrl", 4))
208
1.24k
            mods |= KEY_MODIFIER_CTRL;
209
3.56k
        if (len == 3 && !strncasecmp (name, "Alt", 3))
210
1.02k
            mods |= KEY_MODIFIER_ALT;
211
3.56k
        if (len == 5 && !strncasecmp (name, "Shift", 5))
212
1.29k
            mods |= KEY_MODIFIER_SHIFT;
213
3.56k
        if (len == 4 && !strncasecmp (name, "Meta", 4))
214
0
            mods |= KEY_MODIFIER_META;
215
3.56k
        if (len == 7 && !strncasecmp (name, "Command", 7))
216
0
            mods |= KEY_MODIFIER_COMMAND;
217
218
3.56k
        name += len + 1;
219
3.56k
    }
220
221
6.21k
    struct key_descriptor *d = bsearch (name, s_keys, ARRAY_SIZE(s_keys),
222
6.21k
                                        sizeof (s_keys[0]), keystrcmp);
223
6.21k
    if (d != NULL)
224
2.75k
        code = d->i_code;
225
3.45k
    else if (vlc_towc (name, &code) <= 0)
226
0
        return KEY_UNSET;
227
228
6.21k
    if (code != KEY_UNSET)
229
6.21k
        code |= mods;
230
6.21k
    return code;
231
6.21k
}
232
233
static const char *nooptext (const char *txt)
234
0
{
235
0
    return txt;
236
0
}
237
238
/**
239
 * Format a human-readable and unique representation of a VLC key code
240
 * (including modifiers).
241
 * @param code key code to translate to a string
242
 * @param locale true to get a localized string,
243
 *               false to get a C string suitable for 'vlcrc'
244
 * @return a heap-allocated string, or NULL on error.
245
 */
246
char *vlc_keycode2str (uint_fast32_t code, bool locale)
247
0
{
248
0
    const char *(*tr)(const char *) = locale ? vlc_gettext : nooptext;
249
0
    const char *name;
250
0
    char *str, buf[5];
251
0
    uintptr_t key = code & ~KEY_MODIFIER;
252
253
0
    for (size_t i = 0; i < ARRAY_SIZE(s_keys); i++)
254
0
        if (s_keys[i].i_code == key)
255
0
        {
256
0
            name = s_keys[i].psz;
257
0
            goto found;
258
0
        }
259
260
0
    if (utf8_cp (key, buf) == NULL)
261
0
        return NULL;
262
0
    name = buf;
263
264
0
found:
265
0
    if (asprintf (&str, "%s%s%s%s%s%s",
266
0
                  (code & KEY_MODIFIER_CTRL) ? tr(N_("Ctrl+")) : "",
267
0
                  (code & KEY_MODIFIER_ALT) ? tr(N_("Alt+")) : "",
268
0
                  (code & KEY_MODIFIER_SHIFT) ? tr(N_("Shift+")) : "",
269
0
                  (code & KEY_MODIFIER_META) ? tr(N_("Meta+")) : "",
270
0
                  (code & KEY_MODIFIER_COMMAND) ? tr(N_("Command+")) : "",
271
0
                  tr(name)) == -1)
272
0
        return NULL;
273
0
    return str;
274
0
}
275
276
277
/*** VLC key map ***/
278
279
#define MAXACTION 27
280
static const struct name2action
281
{
282
    char psz[MAXACTION];
283
    vlc_action_id_t id;
284
} s_names2actions[] =
285
{
286
    /* *MUST* be sorted (ASCII order) */
287
    { "aspect-ratio", ACTIONID_ASPECT_RATIO, },
288
    { "audio-track", ACTIONID_AUDIO_TRACK, },
289
    { "audiodelay-down", ACTIONID_AUDIODELAY_DOWN, },
290
    { "audiodelay-up", ACTIONID_AUDIODELAY_UP, },
291
    { "audiodevice-cycle", ACTIONID_AUDIODEVICE_CYCLE, },
292
    { "chapter-next", ACTIONID_CHAPTER_NEXT, },
293
    { "chapter-prev", ACTIONID_CHAPTER_PREV, },
294
    { "clear-playlist", ACTIONID_PLAY_CLEAR, },
295
    { "crop", ACTIONID_CROP, },
296
    { "crop-bottom", ACTIONID_CROP_BOTTOM, },
297
    { "crop-left", ACTIONID_CROP_LEFT, },
298
    { "crop-right", ACTIONID_CROP_RIGHT, },
299
    { "crop-top", ACTIONID_CROP_TOP, },
300
    { "decr-scalefactor", ACTIONID_SCALE_DOWN, },
301
    { "deinterlace", ACTIONID_DEINTERLACE, },
302
    { "deinterlace-mode", ACTIONID_DEINTERLACE_MODE, },
303
    { "disc-menu", ACTIONID_DISC_MENU, },
304
    { "faster", ACTIONID_RATE_FASTER, },
305
    { "frame-next", ACTIONID_FRAME_NEXT, },
306
    { "incr-scalefactor", ACTIONID_SCALE_UP, },
307
    { "intf-boss", ACTIONID_INTF_BOSS, },
308
    { "intf-popup-menu", ACTIONID_INTF_POPUP_MENU, },
309
    { "intf-show", ACTIONID_INTF_TOGGLE_FSC, },
310
    { "jump+extrashort", ACTIONID_JUMP_FORWARD_EXTRASHORT, },
311
    { "jump+long", ACTIONID_JUMP_FORWARD_LONG, },
312
    { "jump+medium", ACTIONID_JUMP_FORWARD_MEDIUM, },
313
    { "jump+short", ACTIONID_JUMP_FORWARD_SHORT, },
314
    { "jump-extrashort", ACTIONID_JUMP_BACKWARD_EXTRASHORT, },
315
    { "jump-long", ACTIONID_JUMP_BACKWARD_LONG, },
316
    { "jump-medium", ACTIONID_JUMP_BACKWARD_MEDIUM, },
317
    { "jump-short", ACTIONID_JUMP_BACKWARD_SHORT, },
318
    { "leave-fullscreen", ACTIONID_LEAVE_FULLSCREEN, },
319
    { "loop", ACTIONID_LOOP, },
320
    { "nav-activate", ACTIONID_NAV_ACTIVATE, },
321
    { "nav-down", ACTIONID_NAV_DOWN, },
322
    { "nav-left", ACTIONID_NAV_LEFT, },
323
    { "nav-right", ACTIONID_NAV_RIGHT, },
324
    { "nav-up", ACTIONID_NAV_UP, },
325
    { "next", ACTIONID_NEXT, },
326
    { "pause", ACTIONID_PAUSE, },
327
    { "play", ACTIONID_PLAY, },
328
    { "play-bookmark1", ACTIONID_PLAY_BOOKMARK1, },
329
    { "play-bookmark10", ACTIONID_PLAY_BOOKMARK10, },
330
    { "play-bookmark2", ACTIONID_PLAY_BOOKMARK2, },
331
    { "play-bookmark3", ACTIONID_PLAY_BOOKMARK3, },
332
    { "play-bookmark4", ACTIONID_PLAY_BOOKMARK4, },
333
    { "play-bookmark5", ACTIONID_PLAY_BOOKMARK5, },
334
    { "play-bookmark6", ACTIONID_PLAY_BOOKMARK6, },
335
    { "play-bookmark7", ACTIONID_PLAY_BOOKMARK7, },
336
    { "play-bookmark8", ACTIONID_PLAY_BOOKMARK8, },
337
    { "play-bookmark9", ACTIONID_PLAY_BOOKMARK9, },
338
    { "play-pause", ACTIONID_PLAY_PAUSE, },
339
    { "position", ACTIONID_POSITION, },
340
    { "prev", ACTIONID_PREV, },
341
    { "program-sid-next", ACTIONID_PROGRAM_SID_NEXT, },
342
    { "program-sid-prev", ACTIONID_PROGRAM_SID_PREV, },
343
    { "projection-toggle", ACTIONID_PROJECTION_TOGGLE, },
344
    { "quit", ACTIONID_QUIT, },
345
    { "random", ACTIONID_RANDOM, },
346
    { "rate-faster-fine", ACTIONID_RATE_FASTER_FINE, },
347
    { "rate-normal", ACTIONID_RATE_NORMAL, },
348
    { "rate-slower-fine", ACTIONID_RATE_SLOWER_FINE, },
349
    { "record", ACTIONID_RECORD, },
350
    { "set-bookmark1", ACTIONID_SET_BOOKMARK1, },
351
    { "set-bookmark10", ACTIONID_SET_BOOKMARK10, },
352
    { "set-bookmark2", ACTIONID_SET_BOOKMARK2, },
353
    { "set-bookmark3", ACTIONID_SET_BOOKMARK3, },
354
    { "set-bookmark4", ACTIONID_SET_BOOKMARK4, },
355
    { "set-bookmark5", ACTIONID_SET_BOOKMARK5, },
356
    { "set-bookmark6", ACTIONID_SET_BOOKMARK6, },
357
    { "set-bookmark7", ACTIONID_SET_BOOKMARK7, },
358
    { "set-bookmark8", ACTIONID_SET_BOOKMARK8, },
359
    { "set-bookmark9", ACTIONID_SET_BOOKMARK9, },
360
    { "slower", ACTIONID_RATE_SLOWER, },
361
    { "snapshot", ACTIONID_SNAPSHOT, },
362
    { "stop", ACTIONID_STOP, },
363
    { "subdelay-down", ACTIONID_SUBDELAY_DOWN, },
364
    { "subdelay-up", ACTIONID_SUBDELAY_UP, },
365
    { "subpos-down", ACTIONID_SUBPOS_DOWN, },
366
    { "subpos-up", ACTIONID_SUBPOS_UP, },
367
    { "subsync-apply", ACTIONID_SUBSYNC_APPLY, },
368
    { "subsync-markaudio", ACTIONID_SUBSYNC_MARKAUDIO, },
369
    { "subsync-marksub", ACTIONID_SUBSYNC_MARKSUB, },
370
    { "subsync-reset", ACTIONID_SUBSYNC_RESET, },
371
    { "subtitle-control-secondary", ACTIONID_SUBTITLE_CONTROL_SECONDARY, },
372
    { "subtitle-revtrack", ACTIONID_SUBTITLE_REVERSE_TRACK, },
373
    { "subtitle-text-scale-down", ACTIONID_SUBTITLE_TEXT_SCALE_DOWN, },
374
    { "subtitle-text-scale-normal", ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL, },
375
    { "subtitle-text-scale-up", ACTIONID_SUBTITLE_TEXT_SCALE_UP, },
376
    { "subtitle-toggle", ACTIONID_SUBTITLE_TOGGLE, },
377
    { "subtitle-track", ACTIONID_SUBTITLE_TRACK, },
378
    { "title-next", ACTIONID_TITLE_NEXT, },
379
    { "title-prev", ACTIONID_TITLE_PREV, },
380
    { "toggle-autoscale", ACTIONID_TOGGLE_AUTOSCALE, },
381
    { "toggle-fullscreen", ACTIONID_TOGGLE_FULLSCREEN, },
382
    { "uncrop-bottom", ACTIONID_UNCROP_BOTTOM, },
383
    { "uncrop-left", ACTIONID_UNCROP_LEFT, },
384
    { "uncrop-right", ACTIONID_UNCROP_RIGHT, },
385
    { "uncrop-top", ACTIONID_UNCROP_TOP, },
386
    { "unzoom", ACTIONID_UNZOOM, },
387
    { "viewpoint-fov-in", ACTIONID_VIEWPOINT_FOV_IN, },
388
    { "viewpoint-fov-out", ACTIONID_VIEWPOINT_FOV_OUT, },
389
    { "viewpoint-roll-anticlock", ACTIONID_VIEWPOINT_ROLL_ANTICLOCK, },
390
    { "viewpoint-roll-clock", ACTIONID_VIEWPOINT_ROLL_CLOCK, },
391
    { "vol-down", ACTIONID_VOL_DOWN, },
392
    { "vol-mute", ACTIONID_VOL_MUTE, },
393
    { "vol-up", ACTIONID_VOL_UP, },
394
    { "wallpaper", ACTIONID_WALLPAPER, },
395
    { "zoom", ACTIONID_ZOOM, },
396
    { "zoom-double", ACTIONID_ZOOM_DOUBLE, },
397
    { "zoom-half", ACTIONID_ZOOM_HALF, },
398
    { "zoom-original", ACTIONID_ZOOM_ORIGINAL, },
399
    { "zoom-quarter", ACTIONID_ZOOM_QUARTER, },
400
};
401
402
struct mapping
403
{
404
    uint32_t     key; ///< Key code
405
    vlc_action_id_t action; ///< Action ID
406
};
407
408
static int keycmp (const void *a, const void *b)
409
39.7k
{
410
39.7k
    const struct mapping *ka = a, *kb = b;
411
412
39.7k
    return (ka->key < kb->key) ? -1 : (ka->key > kb->key) ? +1 : 0;
413
39.7k
}
414
415
struct vlc_actions_t
416
{
417
    void *map; /* Key map */
418
    void *global_map; /* Grabbed/global key map */
419
    const char *ppsz_keys[];
420
};
421
422
static int vlc_key_to_action (vlc_object_t *obj, const char *varname,
423
                              vlc_value_t prevkey, vlc_value_t curkey, void *d)
424
0
{
425
0
    void *const *map = d;
426
0
    const void **pent;
427
0
    uint32_t keycode = curkey.i_int;
428
429
0
    pent = tfind (&keycode, map, keycmp);
430
0
    if (pent == NULL)
431
0
        return VLC_SUCCESS;
432
433
0
    const struct mapping *ent = *pent;
434
435
0
    (void) varname;
436
0
    (void) prevkey;
437
0
    return var_SetInteger (obj, "key-action", ent->action);
438
0
}
439
440
/**
441
 * Adds a mapping from a certain key code to a certain action.
442
 */
443
static int add_mapping (void **map, uint32_t keycode, vlc_action_id_t action)
444
6.42k
{
445
6.42k
    struct mapping *entry = malloc (sizeof (*entry));
446
6.42k
    if (entry == NULL)
447
0
        return ENOMEM;
448
6.42k
    entry->key = keycode;
449
6.42k
    entry->action = action;
450
451
6.42k
    void **pent = tsearch (entry, map, keycmp);
452
6.42k
    if (unlikely(pent == NULL))
453
0
        return ENOMEM;
454
6.42k
    if (*pent != entry)
455
0
    {
456
0
        free (entry);
457
0
        return EEXIST;
458
0
    }
459
6.42k
    return 0;
460
6.42k
}
461
462
static void add_wheel_mapping (void **map, uint32_t kmore, uint32_t kless,
463
                                 int mode)
464
108
{
465
108
    vlc_action_id_t amore = ACTIONID_NONE, aless = ACTIONID_NONE;
466
467
108
    switch (mode)
468
108
    {
469
54
        case 0: /* volume up/down */
470
54
            amore = ACTIONID_COMBO_VOL_FOV_UP;
471
54
            aless = ACTIONID_COMBO_VOL_FOV_DOWN;
472
54
            break;
473
54
        case 2: /* position latter/earlier */
474
54
            amore = ACTIONID_JUMP_FORWARD_EXTRASHORT;
475
54
            aless = ACTIONID_JUMP_BACKWARD_EXTRASHORT;
476
54
            break;
477
0
        case 3: /* position earlier/latter */
478
0
            amore = ACTIONID_JUMP_BACKWARD_EXTRASHORT;
479
0
            aless = ACTIONID_JUMP_FORWARD_EXTRASHORT;
480
0
            break;
481
108
    }
482
483
108
    if (amore != ACTIONID_NONE)
484
108
        add_mapping (map, kmore, amore);
485
108
    if (aless != ACTIONID_NONE)
486
108
        add_mapping (map, kless, aless);
487
108
}
488
489
490
/**
491
 * Sets up all key mappings for a given action.
492
 *
493
 * \param obj the VLC object to inherit keys from and log to
494
 * \param map tree (of struct mapping entries) to write mappings to
495
 * \param confname VLC configuration item to read mappings from
496
 * \param action action ID
497
 */
498
static void init_action (vlc_object_t *obj, void **map,
499
                            const char *confname, vlc_action_id_t action)
500
12.2k
{
501
12.2k
    char *keys = var_InheritString (obj, confname);
502
12.2k
    if (keys == NULL)
503
6.42k
        return;
504
505
5.77k
    for (char *buf, *key = strtok_r (keys, "\t", &buf);
506
11.9k
         key != NULL;
507
6.21k
         key = strtok_r (NULL, "\t", &buf))
508
6.21k
    {
509
6.21k
        uint32_t code = vlc_str2keycode (key);
510
6.21k
        if (code == KEY_UNSET)
511
0
        {
512
0
            msg_Warn (obj, "Key \"%s\" unrecognized", key);
513
0
            continue;
514
0
        }
515
516
6.21k
        if (add_mapping (map, code, action) == EEXIST)
517
6.21k
            msg_Warn (obj, "Key \"%s\" bound to multiple actions", key);
518
6.21k
    }
519
5.77k
    free (keys);
520
5.77k
}
521
522
/**
523
 * Initializes the key map from configuration.
524
 */
525
int libvlc_InternalActionsInit (libvlc_int_t *libvlc)
526
54
{
527
54
    assert(libvlc != NULL);
528
529
54
    vlc_object_t *obj = VLC_OBJECT(libvlc);
530
54
    vlc_actions_t *as = malloc (sizeof (*as) + (1 + ARRAY_SIZE(s_names2actions))
531
54
                      * sizeof (*as->ppsz_keys));
532
533
54
    if (unlikely(as == NULL))
534
0
        return VLC_ENOMEM;
535
54
    as->map = NULL;
536
54
    as->global_map = NULL;
537
538
54
    var_Create (obj, "key-pressed", VLC_VAR_INTEGER);
539
54
    var_Create (obj, "global-key-pressed", VLC_VAR_INTEGER);
540
54
    var_Create (obj, "key-action", VLC_VAR_INTEGER);
541
542
    /* Initialize from configuration */
543
6.15k
    for (size_t i = 0; i < ARRAY_SIZE(s_names2actions); i++)
544
6.10k
    {
545
6.10k
#ifndef NDEBUG
546
6.10k
        if (i > 0
547
6.04k
         && strcmp (s_names2actions[i-1].psz, s_names2actions[i].psz) >= 0)
548
0
        {
549
0
            msg_Err (libvlc, "key-%s and key-%s are not ordered properly",
550
0
                     s_names2actions[i-1].psz, s_names2actions[i].psz);
551
0
            abort ();
552
0
        }
553
6.10k
#endif
554
6.10k
        as->ppsz_keys[i] = s_names2actions[i].psz;
555
556
6.10k
#define STRINGIFY_(x) #x
557
6.10k
#define STRINGIFY(x) STRINGIFY_(x)
558
6.10k
        char name[12 + MAXACTION];
559
560
6.10k
        snprintf (name, sizeof (name), "global-key-%." STRINGIFY(MAXACTION) "s", s_names2actions[i].psz);
561
6.10k
        init_action (obj, &as->map, name + 7, s_names2actions[i].id);
562
6.10k
        init_action (obj, &as->global_map, name, s_names2actions[i].id);
563
6.10k
    }
564
54
    as->ppsz_keys[ARRAY_SIZE(s_names2actions)] = NULL;
565
566
    /* Initialize mouse wheel events */
567
54
    add_wheel_mapping (&as->map, KEY_MOUSEWHEELRIGHT, KEY_MOUSEWHEELLEFT,
568
54
                         var_InheritInteger (obj, "hotkeys-x-wheel-mode"));
569
54
    add_wheel_mapping (&as->map, KEY_MOUSEWHEELUP, KEY_MOUSEWHEELDOWN,
570
54
                         var_InheritInteger (obj, "hotkeys-y-wheel-mode"));
571
572
54
    libvlc_priv(libvlc)->actions = as;
573
54
    var_AddCallback (obj, "key-pressed", vlc_key_to_action, &as->map);
574
54
    var_AddCallback (obj, "global-key-pressed", vlc_key_to_action,
575
54
                     &as->global_map);
576
54
    return VLC_SUCCESS;
577
54
}
578
579
/**
580
 * Destroys the key map.
581
 */
582
void libvlc_InternalActionsClean (libvlc_int_t *libvlc)
583
0
{
584
0
    assert(libvlc != NULL);
585
586
0
    vlc_actions_t *as = libvlc_priv(libvlc)->actions;
587
0
    if (unlikely(as == NULL))
588
0
        return;
589
590
0
    var_DelCallback (libvlc, "global-key-pressed", vlc_key_to_action,
591
0
                     &as->global_map);
592
0
    var_DelCallback (libvlc, "key-pressed", vlc_key_to_action, &as->map);
593
594
0
    tdestroy (as->global_map, free);
595
0
    tdestroy (as->map, free);
596
0
    free (as);
597
0
    libvlc_priv(libvlc)->actions = NULL;
598
0
}
599
600
601
static int actcmp(const void *key, const void *ent)
602
0
{
603
0
    const struct name2action *act = ent;
604
0
    return strcmp(key, act->psz);
605
0
}
606
607
/**
608
 * Get the action ID from the action name in the configuration subsystem.
609
 * @return the action ID or ACTIONID_NONE on error.
610
 */
611
vlc_action_id_t
612
vlc_actions_get_id (const char *name)
613
0
{
614
0
    const struct name2action *act;
615
616
0
    if (strncmp (name, "key-", 4))
617
0
        return ACTIONID_NONE;
618
0
    name += 4;
619
620
0
    act = bsearch(name, s_names2actions, ARRAY_SIZE(s_names2actions),
621
0
                  sizeof(*act), actcmp);
622
0
    return (act != NULL) ? act->id : ACTIONID_NONE;
623
0
}
624
625
#undef vlc_actions_get_keycodes
626
size_t
627
vlc_actions_get_keycodes(vlc_object_t *p_obj, const char *psz_key_name,
628
                        bool b_global, uint_fast32_t **pp_keycodes)
629
0
{
630
0
    assert(strlen( psz_key_name ) <= MAXACTION);
631
0
    char varname[12 /* "global-key-" */ + MAXACTION];
632
0
    snprintf( varname, ARRAY_SIZE(varname), "%skey-%s", b_global ? "global-" : "", psz_key_name );
633
634
0
    *pp_keycodes = NULL;
635
636
0
    char *psz_keys = var_InheritString( p_obj, varname );
637
0
    if( psz_keys == NULL )
638
0
        return 0;
639
640
0
    size_t i_nb_keycodes = 0;
641
0
    for( const char* psz_it = psz_keys; *psz_it; ++psz_it )
642
0
    {
643
0
        if( *psz_it == '\t' )
644
0
            ++i_nb_keycodes;
645
0
    }
646
0
    ++i_nb_keycodes;
647
0
    *pp_keycodes = vlc_alloc( i_nb_keycodes, sizeof( **pp_keycodes ) );
648
0
    if( unlikely( !*pp_keycodes ) )
649
0
    {
650
0
        free( psz_keys );
651
0
        return 0;
652
0
    }
653
0
    size_t i = 0;
654
0
    for( char *buf, *key = strtok_r( psz_keys, "\t", &buf );
655
0
         key != NULL;
656
0
         key = strtok_r( NULL, "\t", &buf ), ++i )
657
0
    {
658
0
        (*pp_keycodes)[i] = vlc_str2keycode( key );
659
0
    }
660
0
    assert( i == i_nb_keycodes );
661
0
    free( psz_keys );
662
0
    return i_nb_keycodes;
663
0
}
664
665
#undef vlc_actions_get_key_names
666
const char* const*
667
vlc_actions_get_key_names(vlc_object_t *p_obj)
668
0
{
669
0
    vlc_actions_t *as = libvlc_priv(vlc_object_instance(p_obj))->actions;
670
0
    return as->ppsz_keys;
671
0
}