1 /******************************************************************************
2 Copyright (C) 2014-2015 by Ruwen Hahn <palana@stunned.de>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program 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 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17
18 #include <inttypes.h>
19
20 #include "obs-internal.h"
21
lock(void)22 static inline bool lock(void)
23 {
24 if (!obs)
25 return false;
26
27 pthread_mutex_lock(&obs->hotkeys.mutex);
28 return true;
29 }
30
unlock(void)31 static inline void unlock(void)
32 {
33 pthread_mutex_unlock(&obs->hotkeys.mutex);
34 }
35
obs_hotkey_get_id(const obs_hotkey_t * key)36 obs_hotkey_id obs_hotkey_get_id(const obs_hotkey_t *key)
37 {
38 return key->id;
39 }
40
obs_hotkey_get_name(const obs_hotkey_t * key)41 const char *obs_hotkey_get_name(const obs_hotkey_t *key)
42 {
43 return key->name;
44 }
45
obs_hotkey_get_description(const obs_hotkey_t * key)46 const char *obs_hotkey_get_description(const obs_hotkey_t *key)
47 {
48 return key->description;
49 }
50
obs_hotkey_get_registerer_type(const obs_hotkey_t * key)51 obs_hotkey_registerer_t obs_hotkey_get_registerer_type(const obs_hotkey_t *key)
52 {
53 return key->registerer_type;
54 }
55
obs_hotkey_get_registerer(const obs_hotkey_t * key)56 void *obs_hotkey_get_registerer(const obs_hotkey_t *key)
57 {
58 return key->registerer;
59 }
60
obs_hotkey_get_pair_partner_id(const obs_hotkey_t * key)61 obs_hotkey_id obs_hotkey_get_pair_partner_id(const obs_hotkey_t *key)
62 {
63 return key->pair_partner_id;
64 }
65
66 obs_key_combination_t
obs_hotkey_binding_get_key_combination(obs_hotkey_binding_t * binding)67 obs_hotkey_binding_get_key_combination(obs_hotkey_binding_t *binding)
68 {
69 return binding->key;
70 }
71
obs_hotkey_binding_get_hotkey_id(obs_hotkey_binding_t * binding)72 obs_hotkey_id obs_hotkey_binding_get_hotkey_id(obs_hotkey_binding_t *binding)
73 {
74 return binding->hotkey_id;
75 }
76
obs_hotkey_binding_get_hotkey(obs_hotkey_binding_t * binding)77 obs_hotkey_t *obs_hotkey_binding_get_hotkey(obs_hotkey_binding_t *binding)
78 {
79 return binding->hotkey;
80 }
81
82 static inline bool find_id(obs_hotkey_id id, size_t *idx);
obs_hotkey_set_name(obs_hotkey_id id,const char * name)83 void obs_hotkey_set_name(obs_hotkey_id id, const char *name)
84 {
85 size_t idx;
86
87 if (!find_id(id, &idx))
88 return;
89
90 obs_hotkey_t *hotkey = &obs->hotkeys.hotkeys.array[idx];
91 bfree(hotkey->name);
92 hotkey->name = bstrdup(name);
93 }
94
obs_hotkey_set_description(obs_hotkey_id id,const char * desc)95 void obs_hotkey_set_description(obs_hotkey_id id, const char *desc)
96 {
97 size_t idx;
98
99 if (!find_id(id, &idx))
100 return;
101
102 obs_hotkey_t *hotkey = &obs->hotkeys.hotkeys.array[idx];
103 bfree(hotkey->description);
104 hotkey->description = bstrdup(desc);
105 }
106
107 static inline bool find_pair_id(obs_hotkey_pair_id id, size_t *idx);
obs_hotkey_pair_set_names(obs_hotkey_pair_id id,const char * name0,const char * name1)108 void obs_hotkey_pair_set_names(obs_hotkey_pair_id id, const char *name0,
109 const char *name1)
110 {
111 size_t idx;
112 obs_hotkey_pair_t pair;
113
114 if (!find_pair_id(id, &idx))
115 return;
116
117 pair = obs->hotkeys.hotkey_pairs.array[idx];
118
119 obs_hotkey_set_name(pair.id[0], name0);
120 obs_hotkey_set_name(pair.id[1], name1);
121 }
122
obs_hotkey_pair_set_descriptions(obs_hotkey_pair_id id,const char * desc0,const char * desc1)123 void obs_hotkey_pair_set_descriptions(obs_hotkey_pair_id id, const char *desc0,
124 const char *desc1)
125 {
126 size_t idx;
127 obs_hotkey_pair_t pair;
128
129 if (!find_pair_id(id, &idx))
130 return;
131
132 pair = obs->hotkeys.hotkey_pairs.array[idx];
133
134 obs_hotkey_set_description(pair.id[0], desc0);
135 obs_hotkey_set_description(pair.id[1], desc1);
136 }
137
hotkey_signal(const char * signal,obs_hotkey_t * hotkey)138 static void hotkey_signal(const char *signal, obs_hotkey_t *hotkey)
139 {
140 calldata_t data;
141 calldata_init(&data);
142 calldata_set_ptr(&data, "key", hotkey);
143
144 signal_handler_signal(obs->hotkeys.signals, signal, &data);
145
146 calldata_free(&data);
147 }
148
149 static inline void fixup_pointers(void);
150 static inline void load_bindings(obs_hotkey_t *hotkey, obs_data_array_t *data);
151
context_add_hotkey(struct obs_context_data * context,obs_hotkey_id id)152 static inline void context_add_hotkey(struct obs_context_data *context,
153 obs_hotkey_id id)
154 {
155 da_push_back(context->hotkeys, &id);
156 }
157
158 static inline obs_hotkey_id
obs_hotkey_register_internal(obs_hotkey_registerer_t type,void * registerer,struct obs_context_data * context,const char * name,const char * description,obs_hotkey_func func,void * data)159 obs_hotkey_register_internal(obs_hotkey_registerer_t type, void *registerer,
160 struct obs_context_data *context, const char *name,
161 const char *description, obs_hotkey_func func,
162 void *data)
163 {
164 if ((obs->hotkeys.next_id + 1) == OBS_INVALID_HOTKEY_ID)
165 blog(LOG_WARNING, "obs-hotkey: Available hotkey ids exhausted");
166
167 obs_hotkey_t *base_addr = obs->hotkeys.hotkeys.array;
168 obs_hotkey_id result = obs->hotkeys.next_id++;
169 obs_hotkey_t *hotkey = da_push_back_new(obs->hotkeys.hotkeys);
170
171 hotkey->id = result;
172 hotkey->name = bstrdup(name);
173 hotkey->description = bstrdup(description);
174 hotkey->func = func;
175 hotkey->data = data;
176 hotkey->registerer_type = type;
177 hotkey->registerer = registerer;
178 hotkey->pair_partner_id = OBS_INVALID_HOTKEY_PAIR_ID;
179
180 if (context) {
181 obs_data_array_t *data =
182 obs_data_get_array(context->hotkey_data, name);
183 load_bindings(hotkey, data);
184 obs_data_array_release(data);
185
186 context_add_hotkey(context, result);
187 }
188
189 if (base_addr != obs->hotkeys.hotkeys.array)
190 fixup_pointers();
191
192 hotkey_signal("hotkey_register", hotkey);
193
194 return result;
195 }
196
obs_hotkey_register_frontend(const char * name,const char * description,obs_hotkey_func func,void * data)197 obs_hotkey_id obs_hotkey_register_frontend(const char *name,
198 const char *description,
199 obs_hotkey_func func, void *data)
200 {
201 if (!lock())
202 return OBS_INVALID_HOTKEY_ID;
203
204 obs_hotkey_id id = obs_hotkey_register_internal(
205 OBS_HOTKEY_REGISTERER_FRONTEND, NULL, NULL, name, description,
206 func, data);
207
208 unlock();
209 return id;
210 }
211
obs_hotkey_register_encoder(obs_encoder_t * encoder,const char * name,const char * description,obs_hotkey_func func,void * data)212 obs_hotkey_id obs_hotkey_register_encoder(obs_encoder_t *encoder,
213 const char *name,
214 const char *description,
215 obs_hotkey_func func, void *data)
216 {
217 if (!encoder || !lock())
218 return OBS_INVALID_HOTKEY_ID;
219
220 obs_hotkey_id id = obs_hotkey_register_internal(
221 OBS_HOTKEY_REGISTERER_ENCODER,
222 obs_encoder_get_weak_encoder(encoder), &encoder->context, name,
223 description, func, data);
224
225 unlock();
226 return id;
227 }
228
obs_hotkey_register_output(obs_output_t * output,const char * name,const char * description,obs_hotkey_func func,void * data)229 obs_hotkey_id obs_hotkey_register_output(obs_output_t *output, const char *name,
230 const char *description,
231 obs_hotkey_func func, void *data)
232 {
233 if (!output || !lock())
234 return OBS_INVALID_HOTKEY_ID;
235
236 obs_hotkey_id id = obs_hotkey_register_internal(
237 OBS_HOTKEY_REGISTERER_OUTPUT,
238 obs_output_get_weak_output(output), &output->context, name,
239 description, func, data);
240
241 unlock();
242 return id;
243 }
244
obs_hotkey_register_service(obs_service_t * service,const char * name,const char * description,obs_hotkey_func func,void * data)245 obs_hotkey_id obs_hotkey_register_service(obs_service_t *service,
246 const char *name,
247 const char *description,
248 obs_hotkey_func func, void *data)
249 {
250 if (!service || !lock())
251 return OBS_INVALID_HOTKEY_ID;
252
253 obs_hotkey_id id = obs_hotkey_register_internal(
254 OBS_HOTKEY_REGISTERER_SERVICE,
255 obs_service_get_weak_service(service), &service->context, name,
256 description, func, data);
257
258 unlock();
259 return id;
260 }
261
obs_hotkey_register_source(obs_source_t * source,const char * name,const char * description,obs_hotkey_func func,void * data)262 obs_hotkey_id obs_hotkey_register_source(obs_source_t *source, const char *name,
263 const char *description,
264 obs_hotkey_func func, void *data)
265 {
266 if (!source || source->context.private || !lock())
267 return OBS_INVALID_HOTKEY_ID;
268
269 obs_hotkey_id id = obs_hotkey_register_internal(
270 OBS_HOTKEY_REGISTERER_SOURCE,
271 obs_source_get_weak_source(source), &source->context, name,
272 description, func, data);
273
274 unlock();
275 return id;
276 }
277
278 static inline void fixup_pair_pointers(void);
279
create_hotkey_pair(struct obs_context_data * context,obs_hotkey_active_func func0,obs_hotkey_active_func func1,void * data0,void * data1)280 static obs_hotkey_pair_t *create_hotkey_pair(struct obs_context_data *context,
281 obs_hotkey_active_func func0,
282 obs_hotkey_active_func func1,
283 void *data0, void *data1)
284 {
285 if ((obs->hotkeys.next_pair_id + 1) == OBS_INVALID_HOTKEY_PAIR_ID)
286 blog(LOG_WARNING, "obs-hotkey: Available hotkey pair ids "
287 "exhausted");
288
289 obs_hotkey_pair_t *base_addr = obs->hotkeys.hotkey_pairs.array;
290 obs_hotkey_pair_t *pair = da_push_back_new(obs->hotkeys.hotkey_pairs);
291
292 pair->pair_id = obs->hotkeys.next_pair_id++;
293 pair->func[0] = func0;
294 pair->func[1] = func1;
295 pair->id[0] = OBS_INVALID_HOTKEY_ID;
296 pair->id[1] = OBS_INVALID_HOTKEY_ID;
297 pair->data[0] = data0;
298 pair->data[1] = data1;
299
300 if (context)
301 da_push_back(context->hotkey_pairs, &pair->pair_id);
302
303 if (base_addr != obs->hotkeys.hotkey_pairs.array)
304 fixup_pair_pointers();
305
306 return pair;
307 }
308
obs_hotkey_pair_first_func(void * data,obs_hotkey_id id,obs_hotkey_t * hotkey,bool pressed)309 static void obs_hotkey_pair_first_func(void *data, obs_hotkey_id id,
310 obs_hotkey_t *hotkey, bool pressed)
311 {
312 UNUSED_PARAMETER(id);
313
314 obs_hotkey_pair_t *pair = data;
315 if (pair->pressed1)
316 return;
317
318 if (pair->pressed0 && !pressed)
319 pair->pressed0 = false;
320 else if (pair->func[0](pair->data[0], pair->pair_id, hotkey, pressed))
321 pair->pressed0 = pressed;
322 }
323
obs_hotkey_pair_second_func(void * data,obs_hotkey_id id,obs_hotkey_t * hotkey,bool pressed)324 static void obs_hotkey_pair_second_func(void *data, obs_hotkey_id id,
325 obs_hotkey_t *hotkey, bool pressed)
326 {
327 UNUSED_PARAMETER(id);
328
329 obs_hotkey_pair_t *pair = data;
330 if (pair->pressed0)
331 return;
332
333 if (pair->pressed1 && !pressed)
334 pair->pressed1 = false;
335 else if (pair->func[1](pair->data[1], pair->pair_id, hotkey, pressed))
336 pair->pressed1 = pressed;
337 }
338
339 static inline bool find_id(obs_hotkey_id id, size_t *idx);
register_hotkey_pair_internal(obs_hotkey_registerer_t type,void * registerer,void * (* weak_ref)(void *),struct obs_context_data * context,const char * name0,const char * description0,const char * name1,const char * description1,obs_hotkey_active_func func0,obs_hotkey_active_func func1,void * data0,void * data1)340 static obs_hotkey_pair_id register_hotkey_pair_internal(
341 obs_hotkey_registerer_t type, void *registerer,
342 void *(*weak_ref)(void *), struct obs_context_data *context,
343 const char *name0, const char *description0, const char *name1,
344 const char *description1, obs_hotkey_active_func func0,
345 obs_hotkey_active_func func1, void *data0, void *data1)
346 {
347
348 if (!lock())
349 return OBS_INVALID_HOTKEY_PAIR_ID;
350
351 obs_hotkey_pair_t *pair =
352 create_hotkey_pair(context, func0, func1, data0, data1);
353
354 pair->id[0] = obs_hotkey_register_internal(type, weak_ref(registerer),
355 context, name0, description0,
356 obs_hotkey_pair_first_func,
357 pair);
358
359 pair->id[1] = obs_hotkey_register_internal(type, weak_ref(registerer),
360 context, name1, description1,
361 obs_hotkey_pair_second_func,
362 pair);
363
364 size_t idx;
365 if (find_id(pair->id[0], &idx))
366 obs->hotkeys.hotkeys.array[idx].pair_partner_id = pair->id[1];
367
368 if (find_id(pair->id[1], &idx))
369 obs->hotkeys.hotkeys.array[idx].pair_partner_id = pair->id[0];
370
371 obs_hotkey_pair_id id = pair->pair_id;
372
373 unlock();
374 return id;
375 }
376
obs_id_(void * id_)377 static inline void *obs_id_(void *id_)
378 {
379 return id_;
380 }
381
obs_hotkey_pair_register_frontend(const char * name0,const char * description0,const char * name1,const char * description1,obs_hotkey_active_func func0,obs_hotkey_active_func func1,void * data0,void * data1)382 obs_hotkey_pair_id obs_hotkey_pair_register_frontend(
383 const char *name0, const char *description0, const char *name1,
384 const char *description1, obs_hotkey_active_func func0,
385 obs_hotkey_active_func func1, void *data0, void *data1)
386 {
387 return register_hotkey_pair_internal(OBS_HOTKEY_REGISTERER_FRONTEND,
388 NULL, obs_id_, NULL, name0,
389 description0, name1, description1,
390 func0, func1, data0, data1);
391 }
392
weak_encoder_ref(void * ref)393 static inline void *weak_encoder_ref(void *ref)
394 {
395 return obs_encoder_get_weak_encoder(ref);
396 }
397
obs_hotkey_pair_register_encoder(obs_encoder_t * encoder,const char * name0,const char * description0,const char * name1,const char * description1,obs_hotkey_active_func func0,obs_hotkey_active_func func1,void * data0,void * data1)398 obs_hotkey_pair_id obs_hotkey_pair_register_encoder(
399 obs_encoder_t *encoder, const char *name0, const char *description0,
400 const char *name1, const char *description1,
401 obs_hotkey_active_func func0, obs_hotkey_active_func func1, void *data0,
402 void *data1)
403 {
404 if (!encoder)
405 return OBS_INVALID_HOTKEY_PAIR_ID;
406 return register_hotkey_pair_internal(OBS_HOTKEY_REGISTERER_ENCODER,
407 encoder, weak_encoder_ref,
408 &encoder->context, name0,
409 description0, name1, description1,
410 func0, func1, data0, data1);
411 }
412
weak_output_ref(void * ref)413 static inline void *weak_output_ref(void *ref)
414 {
415 return obs_output_get_weak_output(ref);
416 }
417
obs_hotkey_pair_register_output(obs_output_t * output,const char * name0,const char * description0,const char * name1,const char * description1,obs_hotkey_active_func func0,obs_hotkey_active_func func1,void * data0,void * data1)418 obs_hotkey_pair_id obs_hotkey_pair_register_output(
419 obs_output_t *output, const char *name0, const char *description0,
420 const char *name1, const char *description1,
421 obs_hotkey_active_func func0, obs_hotkey_active_func func1, void *data0,
422 void *data1)
423 {
424 if (!output)
425 return OBS_INVALID_HOTKEY_PAIR_ID;
426 return register_hotkey_pair_internal(OBS_HOTKEY_REGISTERER_OUTPUT,
427 output, weak_output_ref,
428 &output->context, name0,
429 description0, name1, description1,
430 func0, func1, data0, data1);
431 }
432
weak_service_ref(void * ref)433 static inline void *weak_service_ref(void *ref)
434 {
435 return obs_service_get_weak_service(ref);
436 }
437
obs_hotkey_pair_register_service(obs_service_t * service,const char * name0,const char * description0,const char * name1,const char * description1,obs_hotkey_active_func func0,obs_hotkey_active_func func1,void * data0,void * data1)438 obs_hotkey_pair_id obs_hotkey_pair_register_service(
439 obs_service_t *service, const char *name0, const char *description0,
440 const char *name1, const char *description1,
441 obs_hotkey_active_func func0, obs_hotkey_active_func func1, void *data0,
442 void *data1)
443 {
444 if (!service)
445 return OBS_INVALID_HOTKEY_PAIR_ID;
446 return register_hotkey_pair_internal(OBS_HOTKEY_REGISTERER_SERVICE,
447 service, weak_service_ref,
448 &service->context, name0,
449 description0, name1, description1,
450 func0, func1, data0, data1);
451 }
452
weak_source_ref(void * ref)453 static inline void *weak_source_ref(void *ref)
454 {
455 return obs_source_get_weak_source(ref);
456 }
457
obs_hotkey_pair_register_source(obs_source_t * source,const char * name0,const char * description0,const char * name1,const char * description1,obs_hotkey_active_func func0,obs_hotkey_active_func func1,void * data0,void * data1)458 obs_hotkey_pair_id obs_hotkey_pair_register_source(
459 obs_source_t *source, const char *name0, const char *description0,
460 const char *name1, const char *description1,
461 obs_hotkey_active_func func0, obs_hotkey_active_func func1, void *data0,
462 void *data1)
463 {
464 if (!source)
465 return OBS_INVALID_HOTKEY_PAIR_ID;
466 return register_hotkey_pair_internal(OBS_HOTKEY_REGISTERER_SOURCE,
467 source, weak_source_ref,
468 &source->context, name0,
469 description0, name1, description1,
470 func0, func1, data0, data1);
471 }
472
473 typedef bool (*obs_hotkey_internal_enum_func)(void *data, size_t idx,
474 obs_hotkey_t *hotkey);
475
enum_hotkeys(obs_hotkey_internal_enum_func func,void * data)476 static inline void enum_hotkeys(obs_hotkey_internal_enum_func func, void *data)
477 {
478 const size_t num = obs->hotkeys.hotkeys.num;
479 obs_hotkey_t *array = obs->hotkeys.hotkeys.array;
480 for (size_t i = 0; i < num; i++) {
481 if (!func(data, i, &array[i]))
482 break;
483 }
484 }
485
486 typedef bool (*obs_hotkey_pair_internal_enum_func)(size_t idx,
487 obs_hotkey_pair_t *pair,
488 void *data);
489
enum_hotkey_pairs(obs_hotkey_pair_internal_enum_func func,void * data)490 static inline void enum_hotkey_pairs(obs_hotkey_pair_internal_enum_func func,
491 void *data)
492 {
493 const size_t num = obs->hotkeys.hotkey_pairs.num;
494 obs_hotkey_pair_t *array = obs->hotkeys.hotkey_pairs.array;
495 for (size_t i = 0; i < num; i++) {
496 if (!func(i, &array[i], data))
497 break;
498 }
499 }
500
501 typedef bool (*obs_hotkey_binding_internal_enum_func)(
502 void *data, size_t idx, obs_hotkey_binding_t *binding);
503
enum_bindings(obs_hotkey_binding_internal_enum_func func,void * data)504 static inline void enum_bindings(obs_hotkey_binding_internal_enum_func func,
505 void *data)
506 {
507 const size_t num = obs->hotkeys.bindings.num;
508 obs_hotkey_binding_t *array = obs->hotkeys.bindings.array;
509 for (size_t i = 0; i < num; i++) {
510 if (!func(data, i, &array[i]))
511 break;
512 }
513 }
514
515 struct obs_hotkey_internal_find_forward {
516 obs_hotkey_id id;
517 bool found;
518 size_t idx;
519 };
520
find_id_helper(void * data,size_t idx,obs_hotkey_t * hotkey)521 static inline bool find_id_helper(void *data, size_t idx, obs_hotkey_t *hotkey)
522 {
523 struct obs_hotkey_internal_find_forward *find = data;
524 if (hotkey->id != find->id)
525 return true;
526
527 find->idx = idx;
528 find->found = true;
529 return false;
530 }
531
find_id(obs_hotkey_id id,size_t * idx)532 static inline bool find_id(obs_hotkey_id id, size_t *idx)
533 {
534 struct obs_hotkey_internal_find_forward find = {id, false, 0};
535 enum_hotkeys(find_id_helper, &find);
536 *idx = find.idx;
537 return find.found;
538 }
539
pointer_fixup_func(void * data,size_t idx,obs_hotkey_binding_t * binding)540 static inline bool pointer_fixup_func(void *data, size_t idx,
541 obs_hotkey_binding_t *binding)
542 {
543 UNUSED_PARAMETER(idx);
544 UNUSED_PARAMETER(data);
545
546 size_t idx_;
547 if (!find_id(binding->hotkey_id, &idx_)) {
548 bcrash("obs-hotkey: Could not find hotkey id '%" PRIuMAX "' "
549 "for binding '%s' (modifiers 0x%x)",
550 (uintmax_t)binding->hotkey_id,
551 obs_key_to_name(binding->key.key),
552 binding->key.modifiers);
553 binding->hotkey = NULL;
554 return true;
555 }
556
557 binding->hotkey = &obs->hotkeys.hotkeys.array[idx_];
558
559 return true;
560 }
561
fixup_pointers(void)562 static inline void fixup_pointers(void)
563 {
564 enum_bindings(pointer_fixup_func, NULL);
565 }
566
567 struct obs_hotkey_internal_find_pair_forward {
568 obs_hotkey_pair_id id;
569 bool found;
570 size_t idx;
571 };
572
find_pair_id_helper(size_t idx,obs_hotkey_pair_t * pair,void * data)573 static inline bool find_pair_id_helper(size_t idx, obs_hotkey_pair_t *pair,
574 void *data)
575 {
576 struct obs_hotkey_internal_find_pair_forward *find = data;
577 if (pair->pair_id != find->id)
578 return true;
579
580 find->idx = idx;
581 find->found = true;
582 return false;
583 }
584
find_pair_id(obs_hotkey_pair_id id,size_t * idx)585 static inline bool find_pair_id(obs_hotkey_pair_id id, size_t *idx)
586 {
587 struct obs_hotkey_internal_find_pair_forward find = {id, false, 0};
588 enum_hotkey_pairs(find_pair_id_helper, &find);
589 *idx = find.idx;
590 return find.found;
591 }
592
pair_pointer_fixup_func(size_t idx,obs_hotkey_pair_t * pair,void * data)593 static inline bool pair_pointer_fixup_func(size_t idx, obs_hotkey_pair_t *pair,
594 void *data)
595 {
596 UNUSED_PARAMETER(idx);
597 UNUSED_PARAMETER(data);
598
599 if (find_id(pair->id[0], &idx))
600 obs->hotkeys.hotkeys.array[idx].data = pair;
601
602 if (find_id(pair->id[1], &idx))
603 obs->hotkeys.hotkeys.array[idx].data = pair;
604
605 return true;
606 }
607
fixup_pair_pointers(void)608 static inline void fixup_pair_pointers(void)
609 {
610 enum_hotkey_pairs(pair_pointer_fixup_func, NULL);
611 }
612
enum_context_hotkeys(struct obs_context_data * context,obs_hotkey_internal_enum_func func,void * data)613 static inline void enum_context_hotkeys(struct obs_context_data *context,
614 obs_hotkey_internal_enum_func func,
615 void *data)
616 {
617 const size_t num = context->hotkeys.num;
618 const obs_hotkey_id *array = context->hotkeys.array;
619 obs_hotkey_t *hotkey_array = obs->hotkeys.hotkeys.array;
620 for (size_t i = 0; i < num; i++) {
621 size_t idx;
622 if (!find_id(array[i], &idx))
623 continue;
624
625 if (!func(data, idx, &hotkey_array[idx]))
626 break;
627 }
628 }
629
load_modifier(uint32_t * modifiers,obs_data_t * data,const char * name,uint32_t flag)630 static inline void load_modifier(uint32_t *modifiers, obs_data_t *data,
631 const char *name, uint32_t flag)
632 {
633 if (obs_data_get_bool(data, name))
634 *modifiers |= flag;
635 }
636
create_binding(obs_hotkey_t * hotkey,obs_key_combination_t combo)637 static inline void create_binding(obs_hotkey_t *hotkey,
638 obs_key_combination_t combo)
639 {
640 obs_hotkey_binding_t *binding = da_push_back_new(obs->hotkeys.bindings);
641 if (!binding)
642 return;
643
644 binding->key = combo;
645 binding->hotkey_id = hotkey->id;
646 binding->hotkey = hotkey;
647 }
648
load_binding(obs_hotkey_t * hotkey,obs_data_t * data)649 static inline void load_binding(obs_hotkey_t *hotkey, obs_data_t *data)
650 {
651 if (!hotkey || !data)
652 return;
653
654 obs_key_combination_t combo = {0};
655 uint32_t *modifiers = &combo.modifiers;
656 load_modifier(modifiers, data, "shift", INTERACT_SHIFT_KEY);
657 load_modifier(modifiers, data, "control", INTERACT_CONTROL_KEY);
658 load_modifier(modifiers, data, "alt", INTERACT_ALT_KEY);
659 load_modifier(modifiers, data, "command", INTERACT_COMMAND_KEY);
660
661 combo.key = obs_key_from_name(obs_data_get_string(data, "key"));
662 if (!modifiers &&
663 (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE))
664 return;
665
666 create_binding(hotkey, combo);
667 }
668
load_bindings(obs_hotkey_t * hotkey,obs_data_array_t * data)669 static inline void load_bindings(obs_hotkey_t *hotkey, obs_data_array_t *data)
670 {
671 const size_t count = obs_data_array_count(data);
672 for (size_t i = 0; i < count; i++) {
673 obs_data_t *item = obs_data_array_item(data, i);
674 load_binding(hotkey, item);
675 obs_data_release(item);
676 }
677
678 hotkey_signal("hotkey_bindings_changed", hotkey);
679 }
680
681 static inline void remove_bindings(obs_hotkey_id id);
682
obs_hotkey_load_bindings(obs_hotkey_id id,obs_key_combination_t * combinations,size_t num)683 void obs_hotkey_load_bindings(obs_hotkey_id id,
684 obs_key_combination_t *combinations, size_t num)
685 {
686 size_t idx;
687
688 if (!lock())
689 return;
690
691 if (find_id(id, &idx)) {
692 obs_hotkey_t *hotkey = &obs->hotkeys.hotkeys.array[idx];
693 remove_bindings(id);
694 for (size_t i = 0; i < num; i++)
695 create_binding(hotkey, combinations[i]);
696
697 hotkey_signal("hotkey_bindings_changed", hotkey);
698 }
699 unlock();
700 }
701
obs_hotkey_load(obs_hotkey_id id,obs_data_array_t * data)702 void obs_hotkey_load(obs_hotkey_id id, obs_data_array_t *data)
703 {
704 size_t idx;
705
706 if (!lock())
707 return;
708
709 if (find_id(id, &idx)) {
710 remove_bindings(id);
711 load_bindings(&obs->hotkeys.hotkeys.array[idx], data);
712 }
713 unlock();
714 }
715
enum_load_bindings(void * data,size_t idx,obs_hotkey_t * hotkey)716 static inline bool enum_load_bindings(void *data, size_t idx,
717 obs_hotkey_t *hotkey)
718 {
719 UNUSED_PARAMETER(idx);
720
721 obs_data_array_t *hotkey_data = obs_data_get_array(data, hotkey->name);
722 if (!hotkey_data)
723 return true;
724
725 load_bindings(hotkey, hotkey_data);
726 obs_data_array_release(hotkey_data);
727 return true;
728 }
729
obs_hotkeys_load_encoder(obs_encoder_t * encoder,obs_data_t * hotkeys)730 void obs_hotkeys_load_encoder(obs_encoder_t *encoder, obs_data_t *hotkeys)
731 {
732 if (!encoder || !hotkeys)
733 return;
734 if (!lock())
735 return;
736
737 enum_context_hotkeys(&encoder->context, enum_load_bindings, hotkeys);
738 unlock();
739 }
740
obs_hotkeys_load_output(obs_output_t * output,obs_data_t * hotkeys)741 void obs_hotkeys_load_output(obs_output_t *output, obs_data_t *hotkeys)
742 {
743 if (!output || !hotkeys)
744 return;
745 if (!lock())
746 return;
747
748 enum_context_hotkeys(&output->context, enum_load_bindings, hotkeys);
749 unlock();
750 }
751
obs_hotkeys_load_service(obs_service_t * service,obs_data_t * hotkeys)752 void obs_hotkeys_load_service(obs_service_t *service, obs_data_t *hotkeys)
753 {
754 if (!service || !hotkeys)
755 return;
756 if (!lock())
757 return;
758
759 enum_context_hotkeys(&service->context, enum_load_bindings, hotkeys);
760 unlock();
761 }
762
obs_hotkeys_load_source(obs_source_t * source,obs_data_t * hotkeys)763 void obs_hotkeys_load_source(obs_source_t *source, obs_data_t *hotkeys)
764 {
765 if (!source || !hotkeys)
766 return;
767 if (!lock())
768 return;
769
770 enum_context_hotkeys(&source->context, enum_load_bindings, hotkeys);
771 unlock();
772 }
773
obs_hotkey_pair_load(obs_hotkey_pair_id id,obs_data_array_t * data0,obs_data_array_t * data1)774 void obs_hotkey_pair_load(obs_hotkey_pair_id id, obs_data_array_t *data0,
775 obs_data_array_t *data1)
776 {
777 if ((!data0 && !data1) || !lock())
778 return;
779
780 size_t idx;
781 if (!find_pair_id(id, &idx))
782 goto unlock;
783
784 obs_hotkey_pair_t *pair = &obs->hotkeys.hotkey_pairs.array[idx];
785
786 if (find_id(pair->id[0], &idx)) {
787 remove_bindings(pair->id[0]);
788 load_bindings(&obs->hotkeys.hotkeys.array[idx], data0);
789 }
790 if (find_id(pair->id[1], &idx)) {
791 remove_bindings(pair->id[1]);
792 load_bindings(&obs->hotkeys.hotkeys.array[idx], data1);
793 }
794
795 unlock:
796 unlock();
797 }
798
save_modifier(uint32_t modifiers,obs_data_t * data,const char * name,uint32_t flag)799 static inline void save_modifier(uint32_t modifiers, obs_data_t *data,
800 const char *name, uint32_t flag)
801 {
802 if ((modifiers & flag) == flag)
803 obs_data_set_bool(data, name, true);
804 }
805
806 struct save_bindings_helper_t {
807 obs_data_array_t *array;
808 obs_hotkey_t *hotkey;
809 };
810
save_bindings_helper(void * data,size_t idx,obs_hotkey_binding_t * binding)811 static inline bool save_bindings_helper(void *data, size_t idx,
812 obs_hotkey_binding_t *binding)
813 {
814 UNUSED_PARAMETER(idx);
815 struct save_bindings_helper_t *h = data;
816
817 if (h->hotkey->id != binding->hotkey_id)
818 return true;
819
820 obs_data_t *hotkey = obs_data_create();
821
822 uint32_t modifiers = binding->key.modifiers;
823 save_modifier(modifiers, hotkey, "shift", INTERACT_SHIFT_KEY);
824 save_modifier(modifiers, hotkey, "control", INTERACT_CONTROL_KEY);
825 save_modifier(modifiers, hotkey, "alt", INTERACT_ALT_KEY);
826 save_modifier(modifiers, hotkey, "command", INTERACT_COMMAND_KEY);
827
828 obs_data_set_string(hotkey, "key", obs_key_to_name(binding->key.key));
829
830 obs_data_array_push_back(h->array, hotkey);
831
832 obs_data_release(hotkey);
833
834 return true;
835 }
836
save_hotkey(obs_hotkey_t * hotkey)837 static inline obs_data_array_t *save_hotkey(obs_hotkey_t *hotkey)
838 {
839 obs_data_array_t *data = obs_data_array_create();
840
841 struct save_bindings_helper_t arg = {data, hotkey};
842 enum_bindings(save_bindings_helper, &arg);
843
844 return data;
845 }
846
obs_hotkey_save(obs_hotkey_id id)847 obs_data_array_t *obs_hotkey_save(obs_hotkey_id id)
848 {
849 size_t idx;
850 obs_data_array_t *result = NULL;
851
852 if (!lock())
853 return result;
854
855 if (find_id(id, &idx))
856 result = save_hotkey(&obs->hotkeys.hotkeys.array[idx]);
857 unlock();
858
859 return result;
860 }
861
obs_hotkey_pair_save(obs_hotkey_pair_id id,obs_data_array_t ** p_data0,obs_data_array_t ** p_data1)862 void obs_hotkey_pair_save(obs_hotkey_pair_id id, obs_data_array_t **p_data0,
863 obs_data_array_t **p_data1)
864 {
865 if ((!p_data0 && !p_data1) || !lock())
866 return;
867
868 size_t idx;
869 if (!find_pair_id(id, &idx))
870 goto unlock;
871
872 obs_hotkey_pair_t *pair = &obs->hotkeys.hotkey_pairs.array[idx];
873
874 if (p_data0 && find_id(pair->id[0], &idx)) {
875 *p_data0 = save_hotkey(&obs->hotkeys.hotkeys.array[idx]);
876 }
877 if (p_data1 && find_id(pair->id[1], &idx)) {
878 *p_data1 = save_hotkey(&obs->hotkeys.hotkeys.array[idx]);
879 }
880
881 unlock:
882 unlock();
883 }
884
enum_save_hotkey(void * data,size_t idx,obs_hotkey_t * hotkey)885 static inline bool enum_save_hotkey(void *data, size_t idx,
886 obs_hotkey_t *hotkey)
887 {
888 UNUSED_PARAMETER(idx);
889
890 obs_data_array_t *hotkey_data = save_hotkey(hotkey);
891 obs_data_set_array(data, hotkey->name, hotkey_data);
892 obs_data_array_release(hotkey_data);
893 return true;
894 }
895
save_context_hotkeys(struct obs_context_data * context)896 static inline obs_data_t *save_context_hotkeys(struct obs_context_data *context)
897 {
898 if (!context->hotkeys.num)
899 return NULL;
900
901 obs_data_t *result = obs_data_create();
902 enum_context_hotkeys(context, enum_save_hotkey, result);
903 return result;
904 }
905
obs_hotkeys_save_encoder(obs_encoder_t * encoder)906 obs_data_t *obs_hotkeys_save_encoder(obs_encoder_t *encoder)
907 {
908 obs_data_t *result = NULL;
909
910 if (!lock())
911 return result;
912
913 result = save_context_hotkeys(&encoder->context);
914 unlock();
915
916 return result;
917 }
918
obs_hotkeys_save_output(obs_output_t * output)919 obs_data_t *obs_hotkeys_save_output(obs_output_t *output)
920 {
921 obs_data_t *result = NULL;
922
923 if (!lock())
924 return result;
925
926 result = save_context_hotkeys(&output->context);
927 unlock();
928
929 return result;
930 }
931
obs_hotkeys_save_service(obs_service_t * service)932 obs_data_t *obs_hotkeys_save_service(obs_service_t *service)
933 {
934 obs_data_t *result = NULL;
935
936 if (!lock())
937 return result;
938
939 result = save_context_hotkeys(&service->context);
940 unlock();
941
942 return result;
943 }
944
obs_hotkeys_save_source(obs_source_t * source)945 obs_data_t *obs_hotkeys_save_source(obs_source_t *source)
946 {
947 obs_data_t *result = NULL;
948
949 if (!lock())
950 return result;
951
952 result = save_context_hotkeys(&source->context);
953 unlock();
954
955 return result;
956 }
957
958 struct binding_find_data {
959 obs_hotkey_id id;
960 size_t *idx;
961 bool found;
962 };
963
binding_finder(void * data,size_t idx,obs_hotkey_binding_t * binding)964 static inline bool binding_finder(void *data, size_t idx,
965 obs_hotkey_binding_t *binding)
966 {
967 struct binding_find_data *find = data;
968 if (binding->hotkey_id != find->id)
969 return true;
970
971 *find->idx = idx;
972 find->found = true;
973 return false;
974 }
975
find_binding(obs_hotkey_id id,size_t * idx)976 static inline bool find_binding(obs_hotkey_id id, size_t *idx)
977 {
978 struct binding_find_data data = {id, idx, false};
979 enum_bindings(binding_finder, &data);
980 return data.found;
981 }
982
983 static inline void release_pressed_binding(obs_hotkey_binding_t *binding);
984
remove_bindings(obs_hotkey_id id)985 static inline void remove_bindings(obs_hotkey_id id)
986 {
987 size_t idx;
988 while (find_binding(id, &idx)) {
989 obs_hotkey_binding_t *binding =
990 &obs->hotkeys.bindings.array[idx];
991
992 if (binding->pressed)
993 release_pressed_binding(binding);
994
995 da_erase(obs->hotkeys.bindings, idx);
996 }
997 }
998
release_registerer(obs_hotkey_t * hotkey)999 static void release_registerer(obs_hotkey_t *hotkey)
1000 {
1001 switch (hotkey->registerer_type) {
1002 case OBS_HOTKEY_REGISTERER_FRONTEND:
1003 break;
1004
1005 case OBS_HOTKEY_REGISTERER_ENCODER:
1006 obs_weak_encoder_release(hotkey->registerer);
1007 break;
1008
1009 case OBS_HOTKEY_REGISTERER_OUTPUT:
1010 obs_weak_output_release(hotkey->registerer);
1011 break;
1012
1013 case OBS_HOTKEY_REGISTERER_SERVICE:
1014 obs_weak_service_release(hotkey->registerer);
1015 break;
1016
1017 case OBS_HOTKEY_REGISTERER_SOURCE:
1018 obs_weak_source_release(hotkey->registerer);
1019 break;
1020 }
1021
1022 hotkey->registerer = NULL;
1023 }
1024
unregister_hotkey(obs_hotkey_id id)1025 static inline bool unregister_hotkey(obs_hotkey_id id)
1026 {
1027 if (id >= obs->hotkeys.next_id)
1028 return false;
1029
1030 size_t idx;
1031 if (!find_id(id, &idx))
1032 return false;
1033
1034 obs_hotkey_t *hotkey = &obs->hotkeys.hotkeys.array[idx];
1035
1036 hotkey_signal("hotkey_unregister", hotkey);
1037
1038 release_registerer(hotkey);
1039
1040 bfree(hotkey->name);
1041 bfree(hotkey->description);
1042
1043 if (hotkey->registerer_type == OBS_HOTKEY_REGISTERER_SOURCE)
1044 obs_weak_source_release(hotkey->registerer);
1045
1046 da_erase(obs->hotkeys.hotkeys, idx);
1047 remove_bindings(id);
1048
1049 return obs->hotkeys.hotkeys.num >= idx;
1050 }
1051
unregister_hotkey_pair(obs_hotkey_pair_id id)1052 static inline bool unregister_hotkey_pair(obs_hotkey_pair_id id)
1053 {
1054 if (id >= obs->hotkeys.next_pair_id)
1055 return false;
1056
1057 size_t idx;
1058 if (!find_pair_id(id, &idx))
1059 return false;
1060
1061 obs_hotkey_pair_t *pair = &obs->hotkeys.hotkey_pairs.array[idx];
1062
1063 bool need_fixup = unregister_hotkey(pair->id[0]);
1064 need_fixup = unregister_hotkey(pair->id[1]) || need_fixup;
1065 if (need_fixup)
1066 fixup_pointers();
1067
1068 da_erase(obs->hotkeys.hotkey_pairs, idx);
1069 return obs->hotkeys.hotkey_pairs.num >= idx;
1070 }
1071
obs_hotkey_unregister(obs_hotkey_id id)1072 void obs_hotkey_unregister(obs_hotkey_id id)
1073 {
1074 if (!lock())
1075 return;
1076 if (unregister_hotkey(id))
1077 fixup_pointers();
1078 unlock();
1079 }
1080
obs_hotkey_pair_unregister(obs_hotkey_pair_id id)1081 void obs_hotkey_pair_unregister(obs_hotkey_pair_id id)
1082 {
1083 if (!lock())
1084 return;
1085
1086 if (unregister_hotkey_pair(id))
1087 fixup_pair_pointers();
1088
1089 unlock();
1090 }
1091
context_release_hotkeys(struct obs_context_data * context)1092 static void context_release_hotkeys(struct obs_context_data *context)
1093 {
1094 if (!context->hotkeys.num)
1095 goto cleanup;
1096
1097 bool need_fixup = false;
1098 for (size_t i = 0; i < context->hotkeys.num; i++)
1099 need_fixup = unregister_hotkey(context->hotkeys.array[i]) ||
1100 need_fixup;
1101
1102 if (need_fixup)
1103 fixup_pointers();
1104
1105 cleanup:
1106 da_free(context->hotkeys);
1107 }
1108
context_release_hotkey_pairs(struct obs_context_data * context)1109 static void context_release_hotkey_pairs(struct obs_context_data *context)
1110 {
1111 if (!context->hotkey_pairs.num)
1112 goto cleanup;
1113
1114 bool need_fixup = false;
1115 for (size_t i = 0; i < context->hotkey_pairs.num; i++)
1116 need_fixup = unregister_hotkey_pair(
1117 context->hotkey_pairs.array[i]) ||
1118 need_fixup;
1119
1120 if (need_fixup)
1121 fixup_pair_pointers();
1122
1123 cleanup:
1124 da_free(context->hotkey_pairs);
1125 }
1126
obs_hotkeys_context_release(struct obs_context_data * context)1127 void obs_hotkeys_context_release(struct obs_context_data *context)
1128 {
1129 if (!lock())
1130 return;
1131
1132 context_release_hotkeys(context);
1133 context_release_hotkey_pairs(context);
1134
1135 obs_data_release(context->hotkey_data);
1136 unlock();
1137 }
1138
obs_hotkeys_free(void)1139 void obs_hotkeys_free(void)
1140 {
1141 const size_t num = obs->hotkeys.hotkeys.num;
1142 obs_hotkey_t *hotkeys = obs->hotkeys.hotkeys.array;
1143 for (size_t i = 0; i < num; i++) {
1144 bfree(hotkeys[i].name);
1145 bfree(hotkeys[i].description);
1146
1147 release_registerer(&hotkeys[i]);
1148 }
1149 da_free(obs->hotkeys.bindings);
1150 da_free(obs->hotkeys.hotkeys);
1151 da_free(obs->hotkeys.hotkey_pairs);
1152
1153 for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
1154 if (obs->hotkeys.translations[i]) {
1155 bfree(obs->hotkeys.translations[i]);
1156 obs->hotkeys.translations[i] = NULL;
1157 }
1158 }
1159 }
1160
1161 struct obs_hotkey_internal_enum_forward {
1162 obs_hotkey_enum_func func;
1163 void *data;
1164 };
1165
enum_hotkey(void * data,size_t idx,obs_hotkey_t * hotkey)1166 static inline bool enum_hotkey(void *data, size_t idx, obs_hotkey_t *hotkey)
1167 {
1168 UNUSED_PARAMETER(idx);
1169
1170 struct obs_hotkey_internal_enum_forward *forward = data;
1171 return forward->func(forward->data, hotkey->id, hotkey);
1172 }
1173
obs_enum_hotkeys(obs_hotkey_enum_func func,void * data)1174 void obs_enum_hotkeys(obs_hotkey_enum_func func, void *data)
1175 {
1176 struct obs_hotkey_internal_enum_forward forwarder = {func, data};
1177 if (!lock())
1178 return;
1179
1180 enum_hotkeys(enum_hotkey, &forwarder);
1181 unlock();
1182 }
1183
obs_enum_hotkey_bindings(obs_hotkey_binding_enum_func func,void * data)1184 void obs_enum_hotkey_bindings(obs_hotkey_binding_enum_func func, void *data)
1185 {
1186 if (!lock())
1187 return;
1188
1189 enum_bindings(func, data);
1190 unlock();
1191 }
1192
modifiers_match(obs_hotkey_binding_t * binding,uint32_t modifiers_,bool strict_modifiers)1193 static inline bool modifiers_match(obs_hotkey_binding_t *binding,
1194 uint32_t modifiers_, bool strict_modifiers)
1195 {
1196 uint32_t modifiers = binding->key.modifiers;
1197 return !modifiers ||
1198 (!strict_modifiers && (modifiers & modifiers_) == modifiers) ||
1199 (strict_modifiers && modifiers == modifiers_);
1200 }
1201
is_pressed(obs_key_t key)1202 static inline bool is_pressed(obs_key_t key)
1203 {
1204 return obs_hotkeys_platform_is_pressed(obs->hotkeys.platform_context,
1205 key);
1206 }
1207
press_released_binding(obs_hotkey_binding_t * binding)1208 static inline void press_released_binding(obs_hotkey_binding_t *binding)
1209 {
1210 binding->pressed = true;
1211
1212 obs_hotkey_t *hotkey = binding->hotkey;
1213 if (hotkey->pressed++)
1214 return;
1215
1216 if (!obs->hotkeys.reroute_hotkeys)
1217 hotkey->func(hotkey->data, hotkey->id, hotkey, true);
1218 else if (obs->hotkeys.router_func)
1219 obs->hotkeys.router_func(obs->hotkeys.router_func_data,
1220 hotkey->id, true);
1221 }
1222
release_pressed_binding(obs_hotkey_binding_t * binding)1223 static inline void release_pressed_binding(obs_hotkey_binding_t *binding)
1224 {
1225 binding->pressed = false;
1226
1227 obs_hotkey_t *hotkey = binding->hotkey;
1228 if (--hotkey->pressed)
1229 return;
1230
1231 if (!obs->hotkeys.reroute_hotkeys)
1232 hotkey->func(hotkey->data, hotkey->id, hotkey, false);
1233 else if (obs->hotkeys.router_func)
1234 obs->hotkeys.router_func(obs->hotkeys.router_func_data,
1235 hotkey->id, false);
1236 }
1237
handle_binding(obs_hotkey_binding_t * binding,uint32_t modifiers,bool no_press,bool strict_modifiers,bool * pressed)1238 static inline void handle_binding(obs_hotkey_binding_t *binding,
1239 uint32_t modifiers, bool no_press,
1240 bool strict_modifiers, bool *pressed)
1241 {
1242 bool modifiers_match_ =
1243 modifiers_match(binding, modifiers, strict_modifiers);
1244 bool modifiers_only = binding->key.key == OBS_KEY_NONE;
1245
1246 if (!binding->key.modifiers)
1247 binding->modifiers_match = true;
1248
1249 if (modifiers_only)
1250 pressed = &modifiers_only;
1251
1252 if (!binding->key.modifiers && modifiers_only)
1253 goto reset;
1254
1255 if ((!binding->modifiers_match && !modifiers_only) || !modifiers_match_)
1256 goto reset;
1257
1258 if ((pressed && !*pressed) ||
1259 (!pressed && !is_pressed(binding->key.key)))
1260 goto reset;
1261
1262 if (binding->pressed || no_press)
1263 return;
1264
1265 press_released_binding(binding);
1266 return;
1267
1268 reset:
1269 binding->modifiers_match = modifiers_match_;
1270 if (!binding->pressed)
1271 return;
1272
1273 release_pressed_binding(binding);
1274 }
1275
1276 struct obs_hotkey_internal_inject {
1277 obs_key_combination_t hotkey;
1278 bool pressed;
1279 bool strict_modifiers;
1280 };
1281
inject_hotkey(void * data,size_t idx,obs_hotkey_binding_t * binding)1282 static inline bool inject_hotkey(void *data, size_t idx,
1283 obs_hotkey_binding_t *binding)
1284 {
1285 UNUSED_PARAMETER(idx);
1286 struct obs_hotkey_internal_inject *event = data;
1287
1288 if (modifiers_match(binding, event->hotkey.modifiers,
1289 event->strict_modifiers)) {
1290 bool pressed = binding->key.key == event->hotkey.key &&
1291 event->pressed;
1292 handle_binding(binding, event->hotkey.modifiers, false,
1293 event->strict_modifiers, &pressed);
1294 }
1295
1296 return true;
1297 }
1298
obs_hotkey_inject_event(obs_key_combination_t hotkey,bool pressed)1299 void obs_hotkey_inject_event(obs_key_combination_t hotkey, bool pressed)
1300 {
1301 if (!lock())
1302 return;
1303
1304 struct obs_hotkey_internal_inject event = {
1305 {hotkey.modifiers, hotkey.key},
1306 pressed,
1307 obs->hotkeys.strict_modifiers,
1308 };
1309 enum_bindings(inject_hotkey, &event);
1310 unlock();
1311 }
1312
obs_hotkey_enable_background_press(bool enable)1313 void obs_hotkey_enable_background_press(bool enable)
1314 {
1315 if (!lock())
1316 return;
1317
1318 obs->hotkeys.thread_disable_press = !enable;
1319 unlock();
1320 }
1321
obs_hotkey_enable_strict_modifiers(bool enable)1322 void obs_hotkey_enable_strict_modifiers(bool enable)
1323 {
1324 if (!lock())
1325 return;
1326
1327 obs->hotkeys.strict_modifiers = enable;
1328 unlock();
1329 }
1330
1331 struct obs_query_hotkeys_helper {
1332 uint32_t modifiers;
1333 bool no_press;
1334 bool strict_modifiers;
1335 };
1336
query_hotkey(void * data,size_t idx,obs_hotkey_binding_t * binding)1337 static inline bool query_hotkey(void *data, size_t idx,
1338 obs_hotkey_binding_t *binding)
1339 {
1340 UNUSED_PARAMETER(idx);
1341
1342 struct obs_query_hotkeys_helper *param =
1343 (struct obs_query_hotkeys_helper *)data;
1344 handle_binding(binding, param->modifiers, param->no_press,
1345 param->strict_modifiers, NULL);
1346
1347 return true;
1348 }
1349
query_hotkeys()1350 static inline void query_hotkeys()
1351 {
1352 uint32_t modifiers = 0;
1353 if (is_pressed(OBS_KEY_SHIFT))
1354 modifiers |= INTERACT_SHIFT_KEY;
1355 if (is_pressed(OBS_KEY_CONTROL))
1356 modifiers |= INTERACT_CONTROL_KEY;
1357 if (is_pressed(OBS_KEY_ALT))
1358 modifiers |= INTERACT_ALT_KEY;
1359 if (is_pressed(OBS_KEY_META))
1360 modifiers |= INTERACT_COMMAND_KEY;
1361
1362 struct obs_query_hotkeys_helper param = {
1363 modifiers,
1364 obs->hotkeys.thread_disable_press,
1365 obs->hotkeys.strict_modifiers,
1366 };
1367 enum_bindings(query_hotkey, ¶m);
1368 }
1369
1370 #define NBSP "\xC2\xA0"
1371
obs_hotkey_thread(void * arg)1372 void *obs_hotkey_thread(void *arg)
1373 {
1374 UNUSED_PARAMETER(arg);
1375
1376 os_set_thread_name("libobs: hotkey thread");
1377
1378 const char *hotkey_thread_name =
1379 profile_store_name(obs_get_profiler_name_store(),
1380 "obs_hotkey_thread(%g" NBSP "ms)", 25.);
1381 profile_register_root(hotkey_thread_name, (uint64_t)25000000);
1382
1383 while (os_event_timedwait(obs->hotkeys.stop_event, 25) == ETIMEDOUT) {
1384 if (!lock())
1385 continue;
1386
1387 profile_start(hotkey_thread_name);
1388 query_hotkeys();
1389 profile_end(hotkey_thread_name);
1390
1391 unlock();
1392
1393 profile_reenable_thread();
1394 }
1395 return NULL;
1396 }
1397
obs_hotkey_trigger_routed_callback(obs_hotkey_id id,bool pressed)1398 void obs_hotkey_trigger_routed_callback(obs_hotkey_id id, bool pressed)
1399 {
1400 if (!lock())
1401 return;
1402
1403 if (!obs->hotkeys.reroute_hotkeys)
1404 goto unlock;
1405
1406 size_t idx;
1407 if (!find_id(id, &idx))
1408 goto unlock;
1409
1410 obs_hotkey_t *hotkey = &obs->hotkeys.hotkeys.array[idx];
1411 hotkey->func(hotkey->data, id, hotkey, pressed);
1412
1413 unlock:
1414 unlock();
1415 }
1416
obs_hotkey_set_callback_routing_func(obs_hotkey_callback_router_func func,void * data)1417 void obs_hotkey_set_callback_routing_func(obs_hotkey_callback_router_func func,
1418 void *data)
1419 {
1420 if (!lock())
1421 return;
1422
1423 obs->hotkeys.router_func = func;
1424 obs->hotkeys.router_func_data = data;
1425 unlock();
1426 }
1427
obs_hotkey_enable_callback_rerouting(bool enable)1428 void obs_hotkey_enable_callback_rerouting(bool enable)
1429 {
1430 if (!lock())
1431 return;
1432
1433 obs->hotkeys.reroute_hotkeys = enable;
1434 unlock();
1435 }
1436
obs_set_key_translation(obs_key_t key,const char * translation)1437 static void obs_set_key_translation(obs_key_t key, const char *translation)
1438 {
1439 bfree(obs->hotkeys.translations[key]);
1440 obs->hotkeys.translations[key] = NULL;
1441
1442 if (translation)
1443 obs->hotkeys.translations[key] = bstrdup(translation);
1444 }
1445
obs_hotkeys_set_translations_s(struct obs_hotkeys_translations * translations,size_t size)1446 void obs_hotkeys_set_translations_s(
1447 struct obs_hotkeys_translations *translations, size_t size)
1448 {
1449 #define ADD_TRANSLATION(key_name, var_name) \
1450 if (t.var_name) \
1451 obs_set_key_translation(key_name, t.var_name);
1452
1453 struct obs_hotkeys_translations t = {0};
1454 struct dstr numpad = {0};
1455 struct dstr mouse = {0};
1456 struct dstr button = {0};
1457
1458 if (!translations) {
1459 return;
1460 }
1461
1462 memcpy(&t, translations, (size < sizeof(t)) ? size : sizeof(t));
1463
1464 ADD_TRANSLATION(OBS_KEY_INSERT, insert);
1465 ADD_TRANSLATION(OBS_KEY_DELETE, del);
1466 ADD_TRANSLATION(OBS_KEY_HOME, home);
1467 ADD_TRANSLATION(OBS_KEY_END, end);
1468 ADD_TRANSLATION(OBS_KEY_PAGEUP, page_up);
1469 ADD_TRANSLATION(OBS_KEY_PAGEDOWN, page_down);
1470 ADD_TRANSLATION(OBS_KEY_NUMLOCK, num_lock);
1471 ADD_TRANSLATION(OBS_KEY_SCROLLLOCK, scroll_lock);
1472 ADD_TRANSLATION(OBS_KEY_CAPSLOCK, caps_lock);
1473 ADD_TRANSLATION(OBS_KEY_BACKSPACE, backspace);
1474 ADD_TRANSLATION(OBS_KEY_TAB, tab);
1475 ADD_TRANSLATION(OBS_KEY_PRINT, print);
1476 ADD_TRANSLATION(OBS_KEY_PAUSE, pause);
1477 ADD_TRANSLATION(OBS_KEY_SHIFT, shift);
1478 ADD_TRANSLATION(OBS_KEY_ALT, alt);
1479 ADD_TRANSLATION(OBS_KEY_CONTROL, control);
1480 ADD_TRANSLATION(OBS_KEY_META, meta);
1481 ADD_TRANSLATION(OBS_KEY_MENU, menu);
1482 ADD_TRANSLATION(OBS_KEY_SPACE, space);
1483 ADD_TRANSLATION(OBS_KEY_ESCAPE, escape);
1484 #ifdef __APPLE__
1485 const char *numpad_str = t.apple_keypad_num;
1486 ADD_TRANSLATION(OBS_KEY_NUMSLASH, apple_keypad_divide);
1487 ADD_TRANSLATION(OBS_KEY_NUMASTERISK, apple_keypad_multiply);
1488 ADD_TRANSLATION(OBS_KEY_NUMMINUS, apple_keypad_minus);
1489 ADD_TRANSLATION(OBS_KEY_NUMPLUS, apple_keypad_plus);
1490 ADD_TRANSLATION(OBS_KEY_NUMPERIOD, apple_keypad_decimal);
1491 ADD_TRANSLATION(OBS_KEY_NUMEQUAL, apple_keypad_equal);
1492 #else
1493 const char *numpad_str = t.numpad_num;
1494 ADD_TRANSLATION(OBS_KEY_NUMSLASH, numpad_divide);
1495 ADD_TRANSLATION(OBS_KEY_NUMASTERISK, numpad_multiply);
1496 ADD_TRANSLATION(OBS_KEY_NUMMINUS, numpad_minus);
1497 ADD_TRANSLATION(OBS_KEY_NUMPLUS, numpad_plus);
1498 ADD_TRANSLATION(OBS_KEY_NUMPERIOD, numpad_decimal);
1499 #endif
1500
1501 if (numpad_str) {
1502 dstr_copy(&numpad, numpad_str);
1503 dstr_depad(&numpad);
1504
1505 if (dstr_find(&numpad, "%1") == NULL) {
1506 dstr_cat(&numpad, " %1");
1507 }
1508
1509 #define ADD_NUMPAD_NUM(idx) \
1510 dstr_copy_dstr(&button, &numpad); \
1511 dstr_replace(&button, "%1", #idx); \
1512 obs_set_key_translation(OBS_KEY_NUM##idx, button.array)
1513
1514 ADD_NUMPAD_NUM(0);
1515 ADD_NUMPAD_NUM(1);
1516 ADD_NUMPAD_NUM(2);
1517 ADD_NUMPAD_NUM(3);
1518 ADD_NUMPAD_NUM(4);
1519 ADD_NUMPAD_NUM(5);
1520 ADD_NUMPAD_NUM(6);
1521 ADD_NUMPAD_NUM(7);
1522 ADD_NUMPAD_NUM(8);
1523 ADD_NUMPAD_NUM(9);
1524 }
1525
1526 if (t.mouse_num) {
1527 dstr_copy(&mouse, t.mouse_num);
1528 dstr_depad(&mouse);
1529
1530 if (dstr_find(&mouse, "%1") == NULL) {
1531 dstr_cat(&mouse, " %1");
1532 }
1533
1534 #define ADD_MOUSE_NUM(idx) \
1535 dstr_copy_dstr(&button, &mouse); \
1536 dstr_replace(&button, "%1", #idx); \
1537 obs_set_key_translation(OBS_KEY_MOUSE##idx, button.array)
1538
1539 ADD_MOUSE_NUM(1);
1540 ADD_MOUSE_NUM(2);
1541 ADD_MOUSE_NUM(3);
1542 ADD_MOUSE_NUM(4);
1543 ADD_MOUSE_NUM(5);
1544 ADD_MOUSE_NUM(6);
1545 ADD_MOUSE_NUM(7);
1546 ADD_MOUSE_NUM(8);
1547 ADD_MOUSE_NUM(9);
1548 ADD_MOUSE_NUM(10);
1549 ADD_MOUSE_NUM(11);
1550 ADD_MOUSE_NUM(12);
1551 ADD_MOUSE_NUM(13);
1552 ADD_MOUSE_NUM(14);
1553 ADD_MOUSE_NUM(15);
1554 ADD_MOUSE_NUM(16);
1555 ADD_MOUSE_NUM(17);
1556 ADD_MOUSE_NUM(18);
1557 ADD_MOUSE_NUM(19);
1558 ADD_MOUSE_NUM(20);
1559 ADD_MOUSE_NUM(21);
1560 ADD_MOUSE_NUM(22);
1561 ADD_MOUSE_NUM(23);
1562 ADD_MOUSE_NUM(24);
1563 ADD_MOUSE_NUM(25);
1564 ADD_MOUSE_NUM(26);
1565 ADD_MOUSE_NUM(27);
1566 ADD_MOUSE_NUM(28);
1567 ADD_MOUSE_NUM(29);
1568 }
1569
1570 dstr_free(&numpad);
1571 dstr_free(&mouse);
1572 dstr_free(&button);
1573 }
1574
obs_get_hotkey_translation(obs_key_t key,const char * def)1575 const char *obs_get_hotkey_translation(obs_key_t key, const char *def)
1576 {
1577 if (key == OBS_KEY_NONE) {
1578 return NULL;
1579 }
1580
1581 return obs->hotkeys.translations[key] ? obs->hotkeys.translations[key]
1582 : def;
1583 }
1584
obs_hotkey_update_atomic(obs_hotkey_atomic_update_func func,void * data)1585 void obs_hotkey_update_atomic(obs_hotkey_atomic_update_func func, void *data)
1586 {
1587 if (!lock())
1588 return;
1589
1590 func(data);
1591
1592 unlock();
1593 }
1594
obs_hotkeys_set_audio_hotkeys_translations(const char * mute,const char * unmute,const char * push_to_mute,const char * push_to_talk)1595 void obs_hotkeys_set_audio_hotkeys_translations(const char *mute,
1596 const char *unmute,
1597 const char *push_to_mute,
1598 const char *push_to_talk)
1599 {
1600 #define SET_T(n) \
1601 bfree(obs->hotkeys.n); \
1602 obs->hotkeys.n = bstrdup(n)
1603 SET_T(mute);
1604 SET_T(unmute);
1605 SET_T(push_to_mute);
1606 SET_T(push_to_talk);
1607 #undef SET_T
1608 }
1609
obs_hotkeys_set_sceneitem_hotkeys_translations(const char * show,const char * hide)1610 void obs_hotkeys_set_sceneitem_hotkeys_translations(const char *show,
1611 const char *hide)
1612 {
1613 #define SET_T(n) \
1614 bfree(obs->hotkeys.sceneitem_##n); \
1615 obs->hotkeys.sceneitem_##n = bstrdup(n)
1616 SET_T(show);
1617 SET_T(hide);
1618 #undef SET_T
1619 }
1620