1 // keyboard.c
2 // LiVES
3 // (c) G. Finch 2004 - 2017 <salsaman+lives@gmail.com>
4 // released under the GNU GPL 3 or later
5 // see file ../COPYING for licensing details
6
7 #include <gdk/gdkkeysyms.h>
8
9 #include "main.h"
10 #include "effects.h"
11 #include "callbacks.h"
12
13 #define NEEDS_TRANSLATION LIVES_SPECIAL_MASK
14
15 #ifdef ENABLE_OSC
16 #include "omc-learn.h"
17 #endif
18
19 #ifdef ENABLE_OSC
20
handle_omc_events(void)21 static void handle_omc_events(void) {
22 // check for external controller events
23
24 #ifdef OMC_MIDI_IMPL
25 int midi_check_rate;
26 boolean gotone;
27 #endif
28
29 int i;
30
31 #ifdef OMC_JS_IMPL
32 if (mainw->ext_cntl[EXT_CNTL_JS]) {
33 char *string = js_mangle();
34 if (string) {
35 omc_process_string(OMC_JS, string, FALSE, NULL);
36 lives_free(string);
37 }
38 }
39 #endif // OMC_JS_IMPL
40
41 #ifdef OMC_MIDI_IMPL
42 midi_check_rate = prefs->midi_check_rate;
43 #ifdef ALSA_MIDI
44 if (prefs->use_alsa_midi) midi_check_rate = 1; // because we loop for events in midi_mangle()
45 #endif
46 if (mainw->ext_cntl[EXT_CNTL_MIDI]) {
47 do {
48 gotone = FALSE;
49 for (i = 0; i < midi_check_rate; i++) {
50 char *string = midi_mangle();
51 if (string) {
52 omc_process_string(OMC_MIDI, string, FALSE, NULL);
53 lives_free(string);
54 #ifdef ALSA_MIDI
55 if (prefs->use_alsa_midi) gotone = TRUE;
56 #endif
57 }
58 }
59 } while (gotone);
60 }
61 #endif // OMC_MIDI_IMPL
62 }
63
64 #endif
65
66
ext_triggers_poll(livespointer data)67 boolean ext_triggers_poll(livespointer data) {
68 #ifdef USE_GLIB
69 static int priority = G_PRIORITY_DEFAULT;
70 #endif
71 boolean needs_check = FALSE;
72 if (mainw->is_exiting) return FALSE;
73
74 /* if (!LIVES_IS_PLAYING && !mainw->is_processing && CURRENT_CLIP_IS_VALID && cfile->clip_type == CLIP_TYPE_VIDEODEV) { */
75 /* int pwidth = lives_widget_get_allocation_width(mainw->start_image) - H_RESIZE_ADJUST; */
76 /* int pheight = lives_widget_get_allocation_height(mainw->start_image) - V_RESIZE_ADJUST; */
77 /* mainw->camframe = pull_lives_pixbuf_at_size(mainw->current_file, 1, get_image_ext_for_type(cfile->img_type), */
78 /* cfile->fps, pwidth, pheight, LIVES_INTERP_BEST); */
79 /* load_start_image(1); */
80 /* } */
81
82 // check for external controller events
83 #ifdef ENABLE_JACK
84 #ifdef ENABLE_JACK_TRANSPORT
85 if (mainw->jack_trans_poll) {
86 needs_check = TRUE;
87 lives_jack_poll(); ///< check for jack transport start/stop
88 }
89 #endif
90 #endif
91
92 #ifdef ENABLE_OSC
93 if (mainw->ext_cntl[EXT_CNTL_MIDI] || mainw->ext_cntl[EXT_CNTL_JS]) {
94 needs_check = TRUE;
95 handle_omc_events(); ///< check for playback start triggered by js, MIDI, etc.
96 }
97 #endif
98
99 /// if we have OSC we will poll it here,
100 #ifdef ENABLE_OSC
101 if (prefs->osc_udp_started) {
102 needs_check = TRUE;
103 lives_osc_poll(NULL);
104 }
105 #endif
106
107 #ifdef USE_GLIB
108 if (needs_check) {
109 if (priority != G_PRIORITY_DEFAULT) {
110 g_source_set_priority(g_main_context_find_source_by_id(NULL, mainw->kb_timer), (priority = G_PRIORITY_DEFAULT));
111 }
112 return TRUE;
113 } else {
114 if (priority != G_PRIORITY_DEFAULT_IDLE) {
115 g_source_set_priority(g_main_context_find_source_by_id(NULL, mainw->kb_timer), (priority = G_PRIORITY_LOW));
116 }
117 }
118 #endif
119
120 return TRUE;
121 }
122
123
124 // unused, but left for future reference in case i becomes useful
125 #if defined HAVE_X11
filter_func(LiVESXXEvent * xevent,LiVESXEvent * event,livespointer data)126 LiVESFilterReturn filter_func(LiVESXXEvent *xevent, LiVESXEvent *event, livespointer data) {
127 // filter events at X11 level and act on key press/release
128 uint32_t modifiers = 0;
129 return LIVES_FILTER_CONTINUE; // this is most likely handled in key_press_or_release() now
130 }
131 #endif
132
133
key_press_or_release(LiVESWidget * widget,LiVESXEventKey * event,livespointer user_data)134 boolean key_press_or_release(LiVESWidget *widget, LiVESXEventKey *event, livespointer user_data) {
135 boolean ret = pl_key_function(event->type == LIVES_KEY_PRESS, event->keyval, event->state);
136 //if (ckey !=0 && cached_key == 0) g_print("unaching key\n");
137 return ret;
138 }
139
140
handle_cached_keys(void)141 void handle_cached_keys(void) {
142 // smooth out auto repeat for VJ scratch keys
143 if (cfile->pb_fps == 0.) return;
144 if (cached_key != 0) {
145 lives_accel_groups_activate(LIVES_WIDGET_OBJECT(LIVES_MAIN_WINDOW_WIDGET),
146 (uint32_t)cached_key, (LiVESXModifierType)(cached_mod | LIVES_SPECIAL_MASK));
147 }
148 }
149
150
pl_key_function(boolean down,uint16_t unicode,uint16_t keymod)151 boolean pl_key_function(boolean down, uint16_t unicode, uint16_t keymod) {
152 // translate key events
153 // plugins can also call this with a unicode key to pass key events to LiVES
154 // (via a polling mechanism)
155
156 // mask for ctrl and alt
157 //LiVESXModifierType state=(LiVESXModifierType)(keymod&(LIVES_CONTROL_MASK|LIVES_ALT_MASK));
158
159 // down is a press, up is a release
160
161 keymod = keymod & ~(LIVES_NUMLOCK_MASK);
162
163 if (!down) {
164 // up...
165 if (keymod & NEEDS_TRANSLATION) {
166 switch (unicode) {
167 // some keys need translating when a modifier is held down
168 case (key_left) :
169 case (key_left2):
170 if (cached_key == LIVES_KEY_Left) cached_key = 0;
171 return FALSE;
172 case (key_right) :
173 case (key_right2):
174 if (cached_key == LIVES_KEY_Right) cached_key = 0;
175 return FALSE;
176 case (key_up) :
177 case (key_up2):
178 if (cached_key == LIVES_KEY_Up) cached_key = 0;
179 return FALSE;
180 case (key_down) :
181 case (key_down2):
182 if (cached_key == LIVES_KEY_Down) cached_key = 0;
183 return FALSE;
184 }
185 } else {
186 if (cached_key == unicode) cached_key = 0;
187 }
188 }
189
190 // translate hardware code into gdk keyval, and call any accelerators
191 if (down && (keymod & NEEDS_TRANSLATION)) {
192 switch (unicode) {
193 // some keys need translating when a modifier is held down
194 case (65) :
195 unicode = LIVES_KEY_Space;
196 break;
197 case (22) :
198 unicode = LIVES_KEY_BackSpace;
199 break;
200 case (36) :
201 unicode = LIVES_KEY_Return;
202 break;
203 case (24) :
204 unicode = LIVES_KEY_q;
205 break;
206 case (10) :
207 unicode = LIVES_KEY_1;
208 break;
209 case (11) :
210 unicode = LIVES_KEY_2;
211 break;
212 case (12) :
213 unicode = LIVES_KEY_3;
214 break;
215 case (13) :
216 unicode = LIVES_KEY_4;
217 break;
218 case (14) :
219 unicode = LIVES_KEY_5;
220 break;
221 case (15) :
222 unicode = LIVES_KEY_6;
223 break;
224 case (16) :
225 unicode = LIVES_KEY_7;
226 break;
227 case (17) :
228 unicode = LIVES_KEY_8;
229 break;
230 case (18) :
231 unicode = LIVES_KEY_9;
232 break;
233 case (19) :
234 unicode = LIVES_KEY_0;
235 break;
236 case (67) :
237 unicode = LIVES_KEY_F1;
238 break;
239 case (68) :
240 unicode = LIVES_KEY_F2;
241 break;
242 case (69) :
243 unicode = LIVES_KEY_F3;
244 break;
245 case (70) :
246 unicode = LIVES_KEY_F4;
247 break;
248 case (71) :
249 unicode = LIVES_KEY_F5;
250 break;
251 case (72) :
252 unicode = LIVES_KEY_F6;
253 break;
254 case (73) :
255 unicode = LIVES_KEY_F7;
256 break;
257 case (74) :
258 unicode = LIVES_KEY_F8;
259 break;
260 case (75) :
261 unicode = LIVES_KEY_F9;
262 break;
263 case (76) :
264 unicode = LIVES_KEY_F10;
265 break;
266 case (95) :
267 unicode = LIVES_KEY_F11;
268 break;
269 case (96) :
270 unicode = LIVES_KEY_F12;
271 break;
272 case (99) :
273 case (112) :
274 unicode = LIVES_KEY_Page_Up;
275 break;
276 case (105) :
277 case (117) :
278 unicode = LIVES_KEY_Page_Down;
279 break;
280
281 // auto repeat keys
282 case (key_left) :
283 case (key_left2):
284 unicode = LIVES_KEY_Left;
285 break;
286 case (key_right) :
287 case (key_right2):
288 unicode = LIVES_KEY_Right;
289 break;
290 case (key_up) :
291 case (key_up2):
292 unicode = LIVES_KEY_Up;
293 break;
294 case (key_down) :
295 case (key_down2):
296 unicode = LIVES_KEY_Down;
297 break;
298 }
299 }
300
301 if (down && (unicode == LIVES_KEY_Left || unicode == LIVES_KEY_Right
302 || unicode == LIVES_KEY_Up || unicode == LIVES_KEY_Down) &&
303 (keymod & LIVES_CONTROL_MASK)) {
304 cached_key = unicode;
305 cached_mod = LIVES_CONTROL_MASK;
306 if (keymod & LIVES_ALT_MASK) {
307 cached_mod |= LIVES_ALT_MASK;
308 }
309 if (keymod & LIVES_SHIFT_MASK) {
310 cached_mod |= LIVES_SHIFT_MASK;
311 }
312 }
313
314 if (down && (unicode == LIVES_KEY_Less || unicode == LIVES_KEY_Greater)) {
315 cached_key = unicode;
316 if (keymod & LIVES_SHIFT_MASK) {
317 cached_mod |= LIVES_SHIFT_MASK;
318 }
319 }
320
321 if (mainw->rte_textparm && !(keymod & (LIVES_ALT_MASK | LIVES_CONTROL_MASK))) {
322 if (unicode == LIVES_KEY_Return || unicode == 13) unicode = '\n'; // CR
323 if (unicode == LIVES_KEY_BackSpace) unicode = 8; // bs
324 if (unicode == LIVES_KEY_Tab || unicode == 9) mainw->rte_textparm = NULL;
325 else if (unicode > 0 && unicode < 256) {
326 if (down) {
327 weed_plant_t *inst;
328 int param_number;
329 int error;
330 char *nval;
331 char *cval = weed_get_string_value(mainw->rte_textparm, WEED_LEAF_VALUE, &error);
332 if (unicode == 8 && strlen(cval) > 0) {
333 cval[strlen(cval) - 1] = 0; // delete 1 char
334 nval = lives_strdup(cval);
335 } else nval = lives_strdup_printf("%s%c", cval, (unsigned char)unicode); // append 1 char
336 lives_free(cval);
337 weed_set_string_value(mainw->rte_textparm, WEED_LEAF_VALUE, nval);
338 inst = weed_get_plantptr_value(mainw->rte_textparm, WEED_LEAF_HOST_INSTANCE, &error);
339 param_number = weed_get_int_value(mainw->rte_textparm, WEED_LEAF_HOST_IDX, &error);
340 //copyto = set_copy_to(inst, param_number, TRUE);
341 if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
342 // if we are recording, add this change to our event_list
343 rec_param_change(inst, param_number);
344 //if (copyto != -1) rec_param_change(inst, copyto);
345 }
346 lives_free(nval);
347 }
348 return TRUE;
349 }
350 }
351
352 return FALSE;
353 }
354
355
356 // key callback functions - ones which have keys and need wrappers
357
slower_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)358 boolean slower_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
359 livespointer user_data) {
360 // special flagbit we add, we want to generate these events from the player not from a real key
361 if (!(mod & LIVES_SPECIAL_MASK)) return TRUE;
362 if (mod & LIVES_SHIFT_MASK) on_slower_pressed(NULL, LIVES_INT_TO_POINTER(SCREEN_AREA_BACKGROUND));
363 else on_slower_pressed(NULL, LIVES_INT_TO_POINTER(SCREEN_AREA_FOREGROUND));
364 return TRUE;
365 }
366
367
less_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)368 boolean less_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
369 livespointer user_data) {
370 // special flagbit we add, we want to generate these events from the player not from a real key
371 if (!(mod & LIVES_SPECIAL_MASK)) return TRUE;
372 on_less_pressed(NULL, user_data);
373 return TRUE;
374 }
375
376
faster_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)377 boolean faster_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
378 livespointer user_data) {
379 // special flagbit we add, we want to generate these events from the player not from a real key
380 if (!(mod & LIVES_SPECIAL_MASK)) return TRUE;
381 if (mod & LIVES_SHIFT_MASK) on_faster_pressed(NULL, LIVES_INT_TO_POINTER(SCREEN_AREA_BACKGROUND));
382 else on_faster_pressed(NULL, LIVES_INT_TO_POINTER(SCREEN_AREA_FOREGROUND));
383 return TRUE;
384 }
385
386
more_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)387 boolean more_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
388 livespointer user_data) {
389 // special flagbit we add, we want to generate these events from the player not from a real key
390 if (!(mod & LIVES_SPECIAL_MASK)) return TRUE;
391 on_more_pressed(NULL, user_data);
392 return TRUE;
393 }
394
395
skip_back_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)396 boolean skip_back_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
397 livespointer user_data) {
398 // special flagbit we add, we want to generate these events from the player not from a real key
399 if (LIVES_IS_PLAYING && !(mod & LIVES_SPECIAL_MASK)) return TRUE;
400 on_back_pressed(NULL, user_data);
401 return TRUE;
402 }
403
404
skip_forward_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)405 boolean skip_forward_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
406 livespointer user_data) {
407 // special flagbit we add, we want to generate these events from the player not from a real key
408 if (LIVES_IS_PLAYING && !(mod & LIVES_SPECIAL_MASK)) return TRUE;
409 on_forward_pressed(NULL, user_data);
410 return TRUE;
411 }
412
413
volup_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)414 boolean volup_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
415 livespointer user_data) {
416 // special flagbit we add, we want to generate these events from the player not from a real key
417 if (!(mod & LIVES_SPECIAL_MASK)) return TRUE;
418 on_volch_pressed(NULL, LIVES_INT_TO_POINTER(LIVES_DIRECTION_UP));
419 return TRUE;
420 }
421
422
voldown_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)423 boolean voldown_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
424 livespointer user_data) {
425 // special flagbit we add, we want to generate these events from the player not from a real key
426 if (!(mod & LIVES_SPECIAL_MASK)) return TRUE;
427 on_volch_pressed(NULL, LIVES_INT_TO_POINTER(LIVES_DIRECTION_DOWN));
428 return TRUE;
429 }
430
431
stop_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)432 boolean stop_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
433 livespointer user_data) {
434 on_stop_activate(NULL, NULL);
435 return TRUE;
436 }
437
438
fullscreen_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)439 boolean fullscreen_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
440 livespointer user_data) {
441 on_full_screen_pressed(NULL, NULL);
442 return TRUE;
443 }
444
445
sepwin_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)446 boolean sepwin_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
447 livespointer user_data) {
448 on_sepwin_pressed(NULL, NULL);
449 return TRUE;
450 }
451
452
loop_cont_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)453 boolean loop_cont_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
454 livespointer user_data) {
455 on_loop_button_activate(NULL, NULL);
456 return TRUE;
457 }
458
459
ping_pong_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)460 boolean ping_pong_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
461 livespointer user_data) {
462 on_ping_pong_activate(NULL, NULL);
463 return TRUE;
464 }
465
466
fade_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)467 boolean fade_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
468 livespointer user_data) {
469 on_fade_pressed(NULL, NULL);
470 return TRUE;
471 }
472
473
showfct_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)474 boolean showfct_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
475 livespointer user_data) {
476 lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->showfct), prefs->hide_framebar);
477 return TRUE;
478 }
479
480
showsubs_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)481 boolean showsubs_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
482 livespointer user_data) {
483 lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->showsubs), !prefs->show_subtitles);
484 return TRUE;
485 }
486
487
loop_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)488 boolean loop_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
489 livespointer user_data) {
490 lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->loop_video), !mainw->loop);
491 return TRUE;
492 }
493
494
dblsize_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)495 boolean dblsize_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
496 livespointer user_data) {
497 on_double_size_pressed(NULL, NULL);
498 return TRUE;
499 }
500
501
rec_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)502 boolean rec_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
503 livespointer user_data) {
504 lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->record_perf),
505 !lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(mainw->record_perf)));
506 return TRUE;
507 }
508
509