It's 2020...
[terminatorX.git] / src / tX_mouse.cc
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2020  Alexander K├Ânig
4  
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9  
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  
18     File: tX_mouse.cc
19  
20     Description: This implements the mouse AND keyboard Input handling in
21                  Grab-Mode.
22 */
23
24 #include <sys/wait.h>
25
26 #define __STDC_FORMAT_MACROS
27 #include <inttypes.h>
28 #include <stdint.h>
29
30 #include <config.h>
31
32 #include <gdk/gdkx.h>
33 #include <gtk/gtk.h>
34
35 #include "tX_mouse.h"
36 #include "tX_mastergui.h"
37 #include "tX_global.h"
38 #include "tX_engine.h"
39 #include "tX_vtt.h"
40 #include <stdlib.h>
41 #include <string.h>
42 #include <math.h>
43
44 #define TX_MOUSE_SPEED_NORMAL 0.05
45 #define TX_MOUSE_SPEED_WARP 250000
46
47 tx_mouse :: tx_mouse()
48 {
49         pointer = NULL;
50         keyboard = NULL;
51         linux_input_channel = NULL;
52         grab_mode = LINUX_INPUT;
53         
54         grabbed=0;
55         warp=TX_MOUSE_SPEED_NORMAL;
56         enable_auto_mnemonics = FALSE;
57
58         last_button_press = 0;
59         last_button_release = 0;
60 }
61
62
63 int tx_mouse :: grab()
64 {       
65         if (grabbed) return 0;
66
67         // release all keys
68         memset(key_pressed, 0, sizeof(key_pressed));
69
70         warp_override=false;
71         
72         if (grab_linux_input()) {
73                 grab_mode = LINUX_INPUT;
74         } else {
75                 grab_mode = FALLBACK;
76         }
77         
78         window =  gtk_widget_get_window(main_window);
79         GdkDisplay* gdk_dpy = gdk_window_get_display(window);
80         GdkDeviceManager *device_manager = gdk_display_get_device_manager(gdk_dpy);
81
82         if (grab_mode == FALLBACK) {
83             enable_compression = gdk_window_get_event_compression(window);
84             gdk_window_set_event_compression(window, False);
85         }
86
87         if (!gdk_dpy) {
88                 fputs("GrabMode Error: couldn't access GDKDisplay.", stderr);
89                 return(ENG_ERR_XOPEN);
90         }
91
92         gtk_window_present(GTK_WINDOW(main_window));
93
94         savedEventMask = gdk_window_get_events(window);
95         GdkEventMask newEventMask = GdkEventMask ((int) savedEventMask | GDK_POINTER_MOTION_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
96         gdk_window_set_events(top_window, newEventMask);
97
98         g_object_get (gtk_widget_get_settings (main_window), "gtk-auto-mnemonics", &enable_auto_mnemonics, NULL);
99
100         if (enable_auto_mnemonics) {
101                 gboolean off = FALSE;
102                 g_object_set (gtk_widget_get_settings (main_window), "gtk-auto-mnemonics", off, NULL);
103         }
104
105         pointer = gdk_device_manager_get_client_pointer(device_manager);
106         GdkGrabStatus grab_status = gdk_device_grab(pointer, top_window, GDK_OWNERSHIP_APPLICATION, FALSE, GdkEventMask (GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK), NULL, GDK_CURRENT_TIME);
107
108         gdk_device_get_position(pointer, &screen, &x_restore, &y_restore);      
109         
110         gint x = gdk_screen_get_width(screen) / 2;
111         gint y = gdk_screen_get_height(screen) / 2;
112         
113         x_abs = x;
114         y_abs = y;
115         
116         gdk_device_warp(pointer, screen, x_abs, y_abs);
117
118         if (grab_status != GDK_GRAB_SUCCESS) {
119                 return(-1);
120         }       
121         
122         GList *list = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
123         for (GList *link = list; link != NULL; link = g_list_next (link)) {
124                 GdkDevice *device = GDK_DEVICE (link->data);
125                 
126                 if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
127                         continue;
128
129                 keyboard = device;
130                 break;
131         }
132         
133         grab_status = gdk_device_grab(keyboard, top_window, GDK_OWNERSHIP_APPLICATION, FALSE, GdkEventMask (GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK), NULL, GDK_CURRENT_TIME);
134
135         if (grab_status != GDK_GRAB_SUCCESS) {
136                 gdk_device_ungrab(pointer, GDK_CURRENT_TIME);
137                 return(-2);
138         }
139
140         cursor = gdk_window_get_cursor(window);
141         gdk_window_set_cursor(window, gdk_cursor_new_for_display(gdk_dpy, GDK_BLANK_CURSOR));
142         
143         grabbed=1;
144         
145         std::list<vtt_class *> :: iterator v;
146         int c=0;
147         
148         for (v=vtt_class::main_list.begin(); v!=vtt_class::main_list.end(); v++) {
149                 if (!(*v)->audio_hidden) {
150                         vtt_class::focus_no(c);
151                         break;
152                 }
153                 c++;
154         }
155
156         warp=TX_MOUSE_SPEED_NORMAL;
157         
158         return 0;
159 }
160
161 void tx_mouse :: ungrab()
162 {
163         if (!grabbed) return;
164
165         tX_debug("tX_mouse::ungrab(): this: %016" PRIxPTR, (uintptr_t) this);
166         
167         gdk_device_ungrab(keyboard, GDK_CURRENT_TIME);
168         gdk_device_ungrab(pointer, GDK_CURRENT_TIME);
169
170         gdk_window_set_cursor(window, cursor);
171
172         vtt_class::unfocus();
173
174         gdk_window_set_events(top_window, savedEventMask);
175
176         if (enable_auto_mnemonics) {
177                 g_object_set (gtk_widget_get_settings (main_window), "gtk-auto-mnemonics", enable_auto_mnemonics, NULL);
178         }
179         
180         if (grab_mode == FALLBACK) {
181             gdk_window_set_event_compression(window, enable_compression);
182         }
183
184         ungrab_linux_input();
185         
186         tX_debug("tX_mouse::ungrab(): done");
187         
188         grabbed=0;
189 }
190
191 GError *tx_mouse::open_channel() {
192         GError *error = NULL;
193
194         linux_input_channel = g_io_channel_new_file("/dev/input/mice", "r", &error);
195         if (linux_input_channel) {
196                 g_io_channel_set_flags(linux_input_channel, G_IO_FLAG_NONBLOCK, NULL);
197                 return 0;
198         } else {
199                 return error;
200         }
201         
202         return NULL;
203 }
204
205 void tx_mouse::close_channel() {
206     if (linux_input_channel) {
207             g_io_channel_shutdown(linux_input_channel, false, NULL);
208             g_io_channel_unref(linux_input_channel);
209             linux_input_channel = NULL;
210     }
211 }
212
213 int tx_mouse::grab_linux_input() {
214         
215         if (linux_input_channel) {
216                 linux_input_watch = g_io_add_watch_full(linux_input_channel, G_PRIORITY_HIGH, G_IO_IN, tx_mouse::linux_input_wrap, this, NULL);
217         } else {
218                 tX_msg("Linux input channel not available, falling back to pointer warping.");
219                 return 0;
220         }
221         return 1;
222 }
223
224 void tx_mouse::ungrab_linux_input() {
225         if (grab_mode == LINUX_INPUT) {
226                 // only remove the watch, we keep the channel as we dropped root and might fail to re-open it
227                 g_source_remove(linux_input_watch);
228         }
229 }
230
231 #define vtt vtt_class::focused_vtt
232
233 void tx_mouse::motion_notify(GtkWidget *widget, GdkEventMotion *eventMotion) {
234         if ((grab_mode == FALLBACK) && vtt) {
235                 gdouble d_x = eventMotion->x_root - x_abs;
236                 gdouble d_y = eventMotion->y_root - y_abs;
237                 
238                 if ((d_x != 0.0) || (d_y != 0.0)) {
239                         //gdouble xnow, ynow;
240                         //gdk_device_get_position_double(pointer, NULL, &xnow, &ynow);
241                         //printf("%lf -> %lf, %lf -> %lf\n", eventMotion->x_root, xnow, eventMotion->y_root, ynow);
242                         gdk_device_warp(pointer, screen, x_abs, y_abs);
243                         
244                         if (warp_override) {
245                                 f_prec value=(fabs(d_x)>fabs(d_y)) ? d_x : d_y;
246                                 vtt->sp_speed.handle_mouse_input(value*globals.mouse_speed*warp);
247                         } else {
248                                 vtt->xy_input((f_prec) d_x*warp, (f_prec) d_y*warp);
249                         }
250                 }
251         }
252 }
253
254 void tx_mouse::linux_input(tx_input_event *event) {
255         if ((grab_mode == LINUX_INPUT) && vtt) {
256                 if ((event->x_motion != 0) || (event->y_motion != 0)) {
257                         // gdk_device_warp(pointer, screen, x_abs, y_abs);
258                         gdouble d_x = event->x_motion;
259                         gdouble d_y = event->y_motion;
260                 
261                         if (warp_override) {
262                                 f_prec value=(fabs(d_x)>fabs(d_y)) ? d_x : d_y;
263                                 vtt->sp_speed.handle_mouse_input(value*globals.mouse_speed*warp);
264                         } else {
265                                 vtt->xy_input((f_prec) d_x*warp, (f_prec) d_y*warp);
266                         }
267                 }
268         }
269 }
270
271 void tx_mouse::button_press(GtkWidget *widget, GdkEventButton *eventButton) {
272         if (vtt) {
273                 switch(eventButton->button) {
274                         case 1: if (vtt->is_playing)
275                                         vtt->set_scratch(1);
276                                 else
277                                         vtt->sp_trigger.receive_input_value(1);
278                                 break;
279                         case 2: vtt->sp_mute.receive_input_value(1); break;
280                         case 3: vtt_class::focus_next(); break;
281                 }
282         }
283 }
284
285 void tx_mouse::button_release(GtkWidget *widget, GdkEventButton *eventButton) {
286         if (vtt) {
287                 switch (eventButton->button) {
288                         case 1: vtt->set_scratch(0); break;
289                         case 2: vtt->sp_mute.receive_input_value(0); break;
290                 }
291         }
292 }
293
294 void tx_mouse::key_press(GtkWidget *widget, GdkEventKey *eventKey) {
295         if (vtt) {
296                 switch(eventKey->keyval) {
297                         case GDK_KEY_space: if (press_key(KEY_space)) { vtt->set_scratch(1); } break;
298                         case GDK_KEY_Escape: if (press_key(KEY_Escape)) { ungrab(); } break;
299
300                         case GDK_KEY_Return: if (press_key(KEY_Return)) { vtt->sp_trigger.receive_input_value(1); } break;
301                         case GDK_KEY_BackSpace: if (press_key(KEY_BackSpace)) { vtt->sp_trigger.receive_input_value(0); } break;
302
303                         case GDK_KEY_Tab: if (press_key(KEY_Tab)) { vtt_class::focus_next(); } break;
304
305                         case GDK_KEY_s: if (press_key(KEY_s)) { vtt->sp_sync_client.receive_input_value(!vtt->is_sync_client); } break;
306
307                         case GDK_KEY_m:  if (press_key(KEY_m)) { vtt->sp_mute.receive_input_value(1); } break;
308                         case GDK_KEY_Control_L:  if (press_key(KEY_Control_L)) { vtt->sp_mute.receive_input_value(1); } break;
309                         case GDK_KEY_Control_R:  if (press_key(KEY_Control_R)) { vtt->sp_mute.receive_input_value(1); } break;
310
311                         case GDK_KEY_Alt_L:  if (press_key(KEY_Alt_L)) { vtt->sp_mute.receive_input_value(0); } break;
312                         case GDK_KEY_Alt_R:  if (press_key(KEY_Alt_R)) { vtt->sp_mute.receive_input_value(0); } break;
313
314                         case GDK_KEY_w:  if (press_key(KEY_w)) {
315                                 vtt->sp_mute.receive_input_value(1);
316                                 warp_override=true;
317                                 warp=((float) vtt->samples_in_buffer)/TX_MOUSE_SPEED_WARP;
318                                 vtt->set_scratch(1);
319                         }
320                         break;
321                         case GDK_KEY_f:  if (press_key(KEY_f)) {
322                                 warp_override=true;
323                                 warp=((float) vtt->samples_in_buffer)/TX_MOUSE_SPEED_WARP;
324                                 vtt->set_scratch(1);
325                         }
326                         break;
327
328                         case GDK_KEY_F1: if (press_key(KEY_F1)) { vtt_class::focus_no(0); } break;
329                         case GDK_KEY_F2: if (press_key(KEY_F2)) { vtt_class::focus_no(1); } break;
330                         case GDK_KEY_F3: if (press_key(KEY_F3)) { vtt_class::focus_no(2); } break;
331                         case GDK_KEY_F4: if (press_key(KEY_F4)) { vtt_class::focus_no(3); } break;
332                         case GDK_KEY_F5: if (press_key(KEY_F5)) { vtt_class::focus_no(4); } break;
333                         case GDK_KEY_F6: if (press_key(KEY_F6)) { vtt_class::focus_no(5); } break;
334                         case GDK_KEY_F7: if (press_key(KEY_F7)) { vtt_class::focus_no(6); } break;
335                         case GDK_KEY_F8: if (press_key(KEY_F8)) { vtt_class::focus_no(7); } break;
336                         case GDK_KEY_F9: if (press_key(KEY_F9)) { vtt_class::focus_no(8); } break;
337                         case GDK_KEY_F10: if (press_key(KEY_F10)) { vtt_class::focus_no(9); } break;
338                         case GDK_KEY_F11: if (press_key(KEY_F11)) { vtt_class::focus_no(10); } break;
339                         case GDK_KEY_F12: if (press_key(KEY_F12)) { vtt_class::focus_no(11); } break;
340                 }
341         }
342 }
343
344 void tx_mouse::key_release(GtkWidget *widget, GdkEventKey *eventKey) {
345         if (vtt) {
346                 switch(eventKey->keyval) {
347                         case GDK_KEY_space: if (release_key(KEY_space)) { vtt->set_scratch(0); } break;
348                         case GDK_KEY_Escape: release_key(KEY_Escape); break;
349
350                         case GDK_KEY_Return: release_key(KEY_Return); break;
351                         case GDK_KEY_BackSpace: release_key(KEY_BackSpace); break;
352
353                         case GDK_KEY_Tab: release_key(KEY_Tab); break;
354
355                         case GDK_KEY_s: release_key(KEY_s); break;
356
357
358                         case GDK_KEY_m: if (release_key(KEY_m)) { vtt->sp_mute.receive_input_value(0); } break;
359                         case GDK_KEY_Control_L: if (release_key(KEY_Control_L)) { vtt->sp_mute.receive_input_value(0); } break;
360                         case GDK_KEY_Control_R: if (release_key(KEY_Control_R)) { vtt->sp_mute.receive_input_value(0); } break;
361
362                         case GDK_KEY_Alt_L: if (release_key(KEY_Alt_L)) { vtt->sp_mute.receive_input_value(1); } break;
363                         case GDK_KEY_Alt_R: if (release_key(KEY_Alt_R)) { vtt->sp_mute.receive_input_value(1); } break;
364
365                         case GDK_KEY_w: if (release_key(KEY_w)) {
366                                 vtt->sp_mute.receive_input_value(0);
367                                 warp=TX_MOUSE_SPEED_NORMAL;
368                                 warp_override=false;
369                                 vtt->set_scratch(0);
370                         }
371                         break;
372
373                         case GDK_KEY_f: if (release_key(KEY_f)) {
374                                 warp=TX_MOUSE_SPEED_NORMAL;
375                                 warp_override=false;
376                                 vtt->set_scratch(0);
377                         }
378                         break;
379
380                         case GDK_KEY_F1: release_key(KEY_F1); break;
381                         case GDK_KEY_F2: release_key(KEY_F2); break;
382                         case GDK_KEY_F3: release_key(KEY_F3); break;
383                         case GDK_KEY_F4: release_key(KEY_F4); break;
384                         case GDK_KEY_F5: release_key(KEY_F5); break;
385                         case GDK_KEY_F6: release_key(KEY_F6); break;
386                         case GDK_KEY_F7: release_key(KEY_F7); break;
387                         case GDK_KEY_F8: release_key(KEY_F8); break;
388                         case GDK_KEY_F9: release_key(KEY_F9); break;
389                         case GDK_KEY_F10: release_key(KEY_F10); break;
390                         case GDK_KEY_F11: release_key(KEY_F11); break;
391                         case GDK_KEY_F12: release_key(KEY_F12); break;
392                 }
393         }
394 }
395
396 gboolean tx_mouse::motion_notify_wrap(GtkWidget *widget, GdkEventMotion *eventMotion, void *data) {
397         tx_mouse* mouse = (tx_mouse *) data;
398         if (mouse->grabbed) {
399                 mouse->motion_notify(widget, eventMotion);
400                 return TRUE;
401         } else {
402                 return FALSE;
403         }
404 }
405
406 gboolean tx_mouse::linux_input_wrap(GIOChannel *source, GIOCondition condition, gpointer data) {
407         if (condition == G_IO_IN) {
408                 tx_input_event sum;
409                 tx_input_event eventbuffer[512];
410                 tx_mouse* mouse = (tx_mouse *) data;
411                 gint fd = g_io_channel_unix_get_fd(mouse->linux_input_channel);
412                 ssize_t bytes_read = read(fd, &eventbuffer, sizeof(eventbuffer));
413                 
414                 //printf("read %lu bytes, %lu events\n", bytes_read, bytes_read / sizeof(tx_input_event));
415                 
416                 sum.x_motion = 0;
417                 sum.y_motion = 0;
418                 
419                 for (ssize_t i = 0; i < bytes_read / ((ssize_t) sizeof(tx_input_event)); i++) {
420                         sum.x_motion += eventbuffer[i].x_motion;
421                         sum.y_motion += eventbuffer[i].y_motion;
422                 }
423                 mouse->linux_input(&sum);
424         }
425         return TRUE;
426 }
427
428 gboolean tx_mouse::button_press_wrap(GtkWidget *widget, GdkEventButton *eventButton, void *data) {
429         tx_mouse* mouse = (tx_mouse *) data;
430         if (mouse->grabbed) {
431                 if (mouse->last_button_press != eventButton->time) {
432                         mouse->last_button_press = eventButton->time;
433
434                         tX_debug("tX_mouse::button-press-event (%u)", eventButton->button);
435                         mouse->button_press(widget, eventButton);
436                 } else {
437                         tX_debug("tX_mouse::button-press-event (%u) identical event skipped", eventButton->button);
438                 }
439                 return TRUE;
440         }
441         return FALSE;
442 }
443
444 gboolean tx_mouse::button_release_wrap(GtkWidget *widget, GdkEventButton *eventButton, void *data) {
445         tx_mouse* mouse = (tx_mouse *) data;
446         if (mouse->grabbed) {
447                 if (mouse->last_button_release != eventButton->time) {
448                         mouse->last_button_release = eventButton->time;
449
450                         tX_debug("tX_mouse::button-release-event (%u)", eventButton->button);
451                         mouse->button_release(widget, eventButton);
452                 } else {
453                         tX_debug("tX_mouse::button-release-event (%u) identical event skipped", eventButton->button);
454                 }
455
456                 return TRUE;
457         }
458
459         return FALSE;
460 }
461
462 gboolean tx_mouse::key_press_wrap(GtkWidget *widget, GdkEventKey *eventKey, void *data) {
463         tx_mouse* mouse = (tx_mouse *) data;
464         if (mouse->grabbed) {
465                 tX_debug("tX_mouse::key-press-event (%u)", eventKey->keyval);
466                 mouse->key_press(widget, eventKey);
467                 return TRUE;
468         } else {
469                 return FALSE;
470         }
471 }
472
473 gboolean tx_mouse::key_release_wrap(GtkWidget *widget, GdkEventKey *eventKey, void *data) {
474         tx_mouse* mouse = (tx_mouse *) data;
475         if (mouse->grabbed) {
476                 tX_debug("tX_mouse::key-release-event (%u)", eventKey->keyval);
477                 mouse->key_release(widget, eventKey);
478                 return TRUE;
479         } else {
480                 return FALSE;
481         }
482 }