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