ee5034cd12472eb73ee7e62dd9129f3d59af54ea
[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         GdkSeat *seat = gdk_display_get_default_seat(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_seat_get_pointer(seat);
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         keyboard = gdk_seat_get_keyboard(seat);
123
124         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);
125
126         if (grab_status != GDK_GRAB_SUCCESS) {
127                 gdk_device_ungrab(pointer, GDK_CURRENT_TIME);
128                 return(-2);
129         }
130
131         cursor = gdk_window_get_cursor(window);
132         gdk_window_set_cursor(window, gdk_cursor_new_for_display(gdk_dpy, GDK_BLANK_CURSOR));
133
134         grabbed=1;
135
136         std::list<vtt_class *> :: iterator v;
137         int c=0;
138
139         for (v=vtt_class::main_list.begin(); v!=vtt_class::main_list.end(); v++) {
140                 if (!(*v)->audio_hidden) {
141                         vtt_class::focus_no(c);
142                         break;
143                 }
144                 c++;
145         }
146
147         warp=TX_MOUSE_SPEED_NORMAL;
148
149         return 0;
150 }
151
152 void tx_mouse :: ungrab()
153 {
154         if (!grabbed) return;
155
156         tX_debug("tX_mouse::ungrab(): this: %016" PRIxPTR, (uintptr_t) this);
157
158         gdk_device_ungrab(keyboard, GDK_CURRENT_TIME);
159         gdk_device_ungrab(pointer, GDK_CURRENT_TIME);
160
161         gdk_window_set_cursor(window, cursor);
162
163         vtt_class::unfocus();
164
165         gdk_window_set_events(top_window, savedEventMask);
166
167         if (enable_auto_mnemonics) {
168                 g_object_set (gtk_widget_get_settings (main_window), "gtk-auto-mnemonics", enable_auto_mnemonics, NULL);
169         }
170
171         if (grab_mode == FALLBACK) {
172             gdk_window_set_event_compression(window, enable_compression);
173         }
174
175         ungrab_linux_input();
176
177         tX_debug("tX_mouse::ungrab(): done");
178
179         grabbed=0;
180 }
181
182 GError *tx_mouse::open_channel() {
183         GError *error = NULL;
184
185         linux_input_channel = g_io_channel_new_file("/dev/input/mice", "r", &error);
186         if (linux_input_channel) {
187                 g_io_channel_set_flags(linux_input_channel, G_IO_FLAG_NONBLOCK, NULL);
188                 return 0;
189         } else {
190                 return error;
191         }
192
193         return NULL;
194 }
195
196 void tx_mouse::close_channel() {
197     if (linux_input_channel) {
198             g_io_channel_shutdown(linux_input_channel, false, NULL);
199             g_io_channel_unref(linux_input_channel);
200             linux_input_channel = NULL;
201     }
202 }
203
204 int tx_mouse::grab_linux_input() {
205
206         if (linux_input_channel) {
207                 linux_input_watch = g_io_add_watch_full(linux_input_channel, G_PRIORITY_HIGH, G_IO_IN, tx_mouse::linux_input_wrap, this, NULL);
208         } else {
209                 tX_msg("Linux input channel not available, falling back to pointer warping.");
210                 return 0;
211         }
212         return 1;
213 }
214
215 void tx_mouse::ungrab_linux_input() {
216         if (grab_mode == LINUX_INPUT) {
217                 // only remove the watch, we keep the channel as we dropped root and might fail to re-open it
218                 g_source_remove(linux_input_watch);
219         }
220 }
221
222 #define vtt vtt_class::focused_vtt
223
224 void tx_mouse::motion_notify(GtkWidget *widget, GdkEventMotion *eventMotion) {
225         if ((grab_mode == FALLBACK) && vtt) {
226                 gdouble d_x = eventMotion->x_root - x_abs;
227                 gdouble d_y = eventMotion->y_root - y_abs;
228
229                 if ((d_x != 0.0) || (d_y != 0.0)) {
230                         //gdouble xnow, ynow;
231                         //gdk_device_get_position_double(pointer, NULL, &xnow, &ynow);
232                         //printf("%lf -> %lf, %lf -> %lf\n", eventMotion->x_root, xnow, eventMotion->y_root, ynow);
233                         gdk_device_warp(pointer, screen, x_abs, y_abs);
234
235                         if (warp_override) {
236                                 f_prec value=(fabs(d_x)>fabs(d_y)) ? d_x : d_y;
237                                 vtt->sp_speed.handle_mouse_input(value*globals.mouse_speed*warp);
238                         } else {
239                                 vtt->xy_input((f_prec) d_x*warp, (f_prec) d_y*warp);
240                         }
241                 }
242         }
243 }
244
245 void tx_mouse::linux_input(tx_input_event *event) {
246         if ((grab_mode == LINUX_INPUT) && vtt) {
247                 if ((event->x_motion != 0) || (event->y_motion != 0)) {
248                         // gdk_device_warp(pointer, screen, x_abs, y_abs);
249                         gdouble d_x = event->x_motion;
250                         gdouble d_y = event->y_motion;
251
252                         if (warp_override) {
253                                 f_prec value=(fabs(d_x)>fabs(d_y)) ? d_x : d_y;
254                                 vtt->sp_speed.handle_mouse_input(value*globals.mouse_speed*warp);
255                         } else {
256                                 vtt->xy_input((f_prec) d_x*warp, (f_prec) d_y*warp);
257                         }
258                 }
259         }
260 }
261
262 void tx_mouse::button_press(GtkWidget *widget, GdkEventButton *eventButton) {
263         if (vtt) {
264                 switch(eventButton->button) {
265                         case 1: if (vtt->is_playing)
266                                         vtt->set_scratch(1);
267                                 else
268                                         vtt->sp_trigger.receive_input_value(1);
269                                 break;
270                         case 2: vtt->sp_mute.receive_input_value(1); break;
271                         case 3: vtt_class::focus_next(); break;
272                 }
273         }
274 }
275
276 void tx_mouse::button_release(GtkWidget *widget, GdkEventButton *eventButton) {
277         if (vtt) {
278                 switch (eventButton->button) {
279                         case 1: vtt->set_scratch(0); break;
280                         case 2: vtt->sp_mute.receive_input_value(0); break;
281                 }
282         }
283 }
284
285 void tx_mouse::key_press(GtkWidget *widget, GdkEventKey *eventKey) {
286         if (vtt) {
287                 switch(eventKey->keyval) {
288                         case GDK_KEY_space: if (press_key(KEY_space)) { vtt->set_scratch(1); } break;
289                         case GDK_KEY_Escape: if (press_key(KEY_Escape)) { ungrab(); } break;
290
291                         case GDK_KEY_Return: if (press_key(KEY_Return)) { vtt->sp_trigger.receive_input_value(1); } break;
292                         case GDK_KEY_BackSpace: if (press_key(KEY_BackSpace)) { vtt->sp_trigger.receive_input_value(0); } break;
293
294                         case GDK_KEY_Tab: if (press_key(KEY_Tab)) { vtt_class::focus_next(); } break;
295
296                         case GDK_KEY_s: if (press_key(KEY_s)) { vtt->sp_sync_client.receive_input_value(!vtt->is_sync_client); } break;
297
298                         case GDK_KEY_m:  if (press_key(KEY_m)) { vtt->sp_mute.receive_input_value(1); } break;
299                         case GDK_KEY_Control_L:  if (press_key(KEY_Control_L)) { vtt->sp_mute.receive_input_value(1); } break;
300                         case GDK_KEY_Control_R:  if (press_key(KEY_Control_R)) { vtt->sp_mute.receive_input_value(1); } break;
301
302                         case GDK_KEY_Alt_L:  if (press_key(KEY_Alt_L)) { vtt->sp_mute.receive_input_value(0); } break;
303                         case GDK_KEY_Alt_R:  if (press_key(KEY_Alt_R)) { vtt->sp_mute.receive_input_value(0); } break;
304
305                         case GDK_KEY_w:  if (press_key(KEY_w)) {
306                                 vtt->sp_mute.receive_input_value(1);
307                                 warp_override=true;
308                                 warp=((float) vtt->samples_in_buffer)/TX_MOUSE_SPEED_WARP;
309                                 vtt->set_scratch(1);
310                         }
311                         break;
312                         case GDK_KEY_f:  if (press_key(KEY_f)) {
313                                 warp_override=true;
314                                 warp=((float) vtt->samples_in_buffer)/TX_MOUSE_SPEED_WARP;
315                                 vtt->set_scratch(1);
316                         }
317                         break;
318
319                         case GDK_KEY_F1: if (press_key(KEY_F1)) { vtt_class::focus_no(0); } break;
320                         case GDK_KEY_F2: if (press_key(KEY_F2)) { vtt_class::focus_no(1); } break;
321                         case GDK_KEY_F3: if (press_key(KEY_F3)) { vtt_class::focus_no(2); } break;
322                         case GDK_KEY_F4: if (press_key(KEY_F4)) { vtt_class::focus_no(3); } break;
323                         case GDK_KEY_F5: if (press_key(KEY_F5)) { vtt_class::focus_no(4); } break;
324                         case GDK_KEY_F6: if (press_key(KEY_F6)) { vtt_class::focus_no(5); } break;
325                         case GDK_KEY_F7: if (press_key(KEY_F7)) { vtt_class::focus_no(6); } break;
326                         case GDK_KEY_F8: if (press_key(KEY_F8)) { vtt_class::focus_no(7); } break;
327                         case GDK_KEY_F9: if (press_key(KEY_F9)) { vtt_class::focus_no(8); } break;
328                         case GDK_KEY_F10: if (press_key(KEY_F10)) { vtt_class::focus_no(9); } break;
329                         case GDK_KEY_F11: if (press_key(KEY_F11)) { vtt_class::focus_no(10); } break;
330                         case GDK_KEY_F12: if (press_key(KEY_F12)) { vtt_class::focus_no(11); } break;
331                 }
332         }
333 }
334
335 void tx_mouse::key_release(GtkWidget *widget, GdkEventKey *eventKey) {
336         if (vtt) {
337                 switch(eventKey->keyval) {
338                         case GDK_KEY_space: if (release_key(KEY_space)) { vtt->set_scratch(0); } break;
339                         case GDK_KEY_Escape: release_key(KEY_Escape); break;
340
341                         case GDK_KEY_Return: release_key(KEY_Return); break;
342                         case GDK_KEY_BackSpace: release_key(KEY_BackSpace); break;
343
344                         case GDK_KEY_Tab: release_key(KEY_Tab); break;
345
346                         case GDK_KEY_s: release_key(KEY_s); break;
347
348
349                         case GDK_KEY_m: if (release_key(KEY_m)) { vtt->sp_mute.receive_input_value(0); } break;
350                         case GDK_KEY_Control_L: if (release_key(KEY_Control_L)) { vtt->sp_mute.receive_input_value(0); } break;
351                         case GDK_KEY_Control_R: if (release_key(KEY_Control_R)) { vtt->sp_mute.receive_input_value(0); } break;
352
353                         case GDK_KEY_Alt_L: if (release_key(KEY_Alt_L)) { vtt->sp_mute.receive_input_value(1); } break;
354                         case GDK_KEY_Alt_R: if (release_key(KEY_Alt_R)) { vtt->sp_mute.receive_input_value(1); } break;
355
356                         case GDK_KEY_w: if (release_key(KEY_w)) {
357                                 vtt->sp_mute.receive_input_value(0);
358                                 warp=TX_MOUSE_SPEED_NORMAL;
359                                 warp_override=false;
360                                 vtt->set_scratch(0);
361                         }
362                         break;
363
364                         case GDK_KEY_f: if (release_key(KEY_f)) {
365                                 warp=TX_MOUSE_SPEED_NORMAL;
366                                 warp_override=false;
367                                 vtt->set_scratch(0);
368                         }
369                         break;
370
371                         case GDK_KEY_F1: release_key(KEY_F1); break;
372                         case GDK_KEY_F2: release_key(KEY_F2); break;
373                         case GDK_KEY_F3: release_key(KEY_F3); break;
374                         case GDK_KEY_F4: release_key(KEY_F4); break;
375                         case GDK_KEY_F5: release_key(KEY_F5); break;
376                         case GDK_KEY_F6: release_key(KEY_F6); break;
377                         case GDK_KEY_F7: release_key(KEY_F7); break;
378                         case GDK_KEY_F8: release_key(KEY_F8); break;
379                         case GDK_KEY_F9: release_key(KEY_F9); break;
380                         case GDK_KEY_F10: release_key(KEY_F10); break;
381                         case GDK_KEY_F11: release_key(KEY_F11); break;
382                         case GDK_KEY_F12: release_key(KEY_F12); break;
383                 }
384         }
385 }
386
387 gboolean tx_mouse::motion_notify_wrap(GtkWidget *widget, GdkEventMotion *eventMotion, void *data) {
388         tx_mouse* mouse = (tx_mouse *) data;
389         if (mouse->grabbed) {
390                 mouse->motion_notify(widget, eventMotion);
391                 return TRUE;
392         } else {
393                 return FALSE;
394         }
395 }
396
397 gboolean tx_mouse::linux_input_wrap(GIOChannel *source, GIOCondition condition, gpointer data) {
398         if (condition == G_IO_IN) {
399                 tx_input_event sum;
400                 tx_input_event eventbuffer[512];
401                 tx_mouse* mouse = (tx_mouse *) data;
402                 gint fd = g_io_channel_unix_get_fd(mouse->linux_input_channel);
403                 ssize_t bytes_read = read(fd, &eventbuffer, sizeof(eventbuffer));
404
405                 //printf("read %lu bytes, %lu events\n", bytes_read, bytes_read / sizeof(tx_input_event));
406
407                 sum.x_motion = 0;
408                 sum.y_motion = 0;
409
410                 for (ssize_t i = 0; i < bytes_read / ((ssize_t) sizeof(tx_input_event)); i++) {
411                         sum.x_motion += eventbuffer[i].x_motion;
412                         sum.y_motion += eventbuffer[i].y_motion;
413                 }
414                 mouse->linux_input(&sum);
415         }
416         return TRUE;
417 }
418
419 gboolean tx_mouse::button_press_wrap(GtkWidget *widget, GdkEventButton *eventButton, void *data) {
420         tx_mouse* mouse = (tx_mouse *) data;
421         if (mouse->grabbed) {
422                 if (mouse->last_button_press != eventButton->time) {
423                         mouse->last_button_press = eventButton->time;
424
425                         tX_debug("tX_mouse::button-press-event (%u)", eventButton->button);
426                         mouse->button_press(widget, eventButton);
427                 } else {
428                         tX_debug("tX_mouse::button-press-event (%u) identical event skipped", eventButton->button);
429                 }
430                 return TRUE;
431         }
432         return FALSE;
433 }
434
435 gboolean tx_mouse::button_release_wrap(GtkWidget *widget, GdkEventButton *eventButton, void *data) {
436         tx_mouse* mouse = (tx_mouse *) data;
437         if (mouse->grabbed) {
438                 if (mouse->last_button_release != eventButton->time) {
439                         mouse->last_button_release = eventButton->time;
440
441                         tX_debug("tX_mouse::button-release-event (%u)", eventButton->button);
442                         mouse->button_release(widget, eventButton);
443                 } else {
444                         tX_debug("tX_mouse::button-release-event (%u) identical event skipped", eventButton->button);
445                 }
446
447                 return TRUE;
448         }
449
450         return FALSE;
451 }
452
453 gboolean tx_mouse::key_press_wrap(GtkWidget *widget, GdkEventKey *eventKey, void *data) {
454         tx_mouse* mouse = (tx_mouse *) data;
455         if (mouse->grabbed) {
456                 tX_debug("tX_mouse::key-press-event (%u)", eventKey->keyval);
457                 mouse->key_press(widget, eventKey);
458                 return TRUE;
459         } else {
460                 return FALSE;
461         }
462 }
463
464 gboolean tx_mouse::key_release_wrap(GtkWidget *widget, GdkEventKey *eventKey, void *data) {
465         tx_mouse* mouse = (tx_mouse *) data;
466         if (mouse->grabbed) {
467                 tX_debug("tX_mouse::key-release-event (%u)", eventKey->keyval);
468                 mouse->key_release(widget, eventKey);
469                 return TRUE;
470         } else {
471                 return FALSE;
472         }
473 }