Complete switch to gdk_seat_* API
[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         display = NULL;
51         window = NULL;
52         seat = NULL;
53         linux_input_channel = NULL;
54         grab_mode = LINUX_INPUT;
55
56         grabbed=0;
57         warp=TX_MOUSE_SPEED_NORMAL;
58         enable_auto_mnemonics = FALSE;
59
60         last_button_press = 0;
61         last_button_release = 0;
62 }
63
64
65 int tx_mouse :: grab()
66 {
67         if (grabbed) return 0;
68
69         // release all keys
70         memset(key_pressed, 0, sizeof(key_pressed));
71
72         warp_override=false;
73
74         if (grab_linux_input()) {
75                 grab_mode = LINUX_INPUT;
76         } else {
77                 grab_mode = FALLBACK;
78         }
79
80         if (window == NULL) {
81                 window = gtk_widget_get_window(main_window);
82                 display = gdk_window_get_display(window);
83                 seat = gdk_display_get_default_seat(display);
84                 cursor = gdk_cursor_new_for_display(display, GDK_BLANK_CURSOR);
85         }
86
87         if (grab_mode == FALLBACK) {
88             enable_compression = gdk_window_get_event_compression(window);
89             gdk_window_set_event_compression(window, FALSE);
90         }
91
92         if (!display) {
93                 fputs("GrabMode Error: couldn't access GDKDisplay.", stderr);
94                 if (grab_mode == FALLBACK) {
95                         gdk_window_set_event_compression(window, enable_compression);
96                 }
97                 return(ENG_ERR_XOPEN);
98         }
99
100         gtk_window_present(GTK_WINDOW(main_window));
101
102         savedEventMask = gdk_window_get_events(window);
103         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);
104         gdk_window_set_events(top_window, newEventMask);
105
106         g_object_get (gtk_widget_get_settings (main_window), "gtk-auto-mnemonics", &enable_auto_mnemonics, NULL);
107
108         if (enable_auto_mnemonics) {
109                 gboolean off = FALSE;
110                 g_object_set (gtk_widget_get_settings (main_window), "gtk-auto-mnemonics", off, NULL);
111         }
112
113         GdkGrabStatus grab_status = gdk_seat_grab(seat, top_window, GdkSeatCapabilities (GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_KEYBOARD), FALSE, cursor, NULL, NULL, NULL);
114
115         if (grab_status != GDK_GRAB_SUCCESS) {
116                 if (grab_mode == FALLBACK) {
117                         gdk_window_set_event_compression(window, enable_compression);
118                 }
119                 return(-1);
120         }
121
122         pointer = gdk_seat_get_pointer(seat);
123         gdk_device_get_position(pointer, &screen, &x_restore, &y_restore);
124         GdkMonitor *monitor = gdk_display_get_monitor_at_point(display, x_restore, y_restore);
125         GdkRectangle rect;
126
127         gdk_monitor_get_geometry(monitor, &rect);
128
129         x_abs = rect.width / 2 * gdk_monitor_get_scale_factor(monitor);
130         y_abs = rect.height / 2 * gdk_monitor_get_scale_factor(monitor);
131
132         gdk_device_warp(pointer, screen, x_abs, y_abs);
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_seat_ungrab(seat);
159
160         vtt_class::unfocus();
161
162         gdk_window_set_events(top_window, savedEventMask);
163
164         if (enable_auto_mnemonics) {
165                 g_object_set (gtk_widget_get_settings (main_window), "gtk-auto-mnemonics", enable_auto_mnemonics, NULL);
166         }
167
168         if (grab_mode == FALLBACK) {
169             gdk_window_set_event_compression(window, enable_compression);
170         }
171
172         ungrab_linux_input();
173
174         tX_debug("tX_mouse::ungrab(): done");
175
176         grabbed=0;
177 }
178
179 GError *tx_mouse::open_channel() {
180         GError *error = NULL;
181
182         linux_input_channel = g_io_channel_new_file("/dev/input/mice", "r", &error);
183         if (linux_input_channel) {
184                 g_io_channel_set_flags(linux_input_channel, G_IO_FLAG_NONBLOCK, NULL);
185                 return 0;
186         } else {
187                 return error;
188         }
189
190         return NULL;
191 }
192
193 void tx_mouse::close_channel() {
194     if (linux_input_channel) {
195             g_io_channel_shutdown(linux_input_channel, false, NULL);
196             g_io_channel_unref(linux_input_channel);
197             linux_input_channel = NULL;
198     }
199 }
200
201 int tx_mouse::grab_linux_input() {
202
203         if (linux_input_channel) {
204                 linux_input_watch = g_io_add_watch_full(linux_input_channel, G_PRIORITY_HIGH, G_IO_IN, tx_mouse::linux_input_wrap, this, NULL);
205         } else {
206                 tX_msg("Linux input channel not available, falling back to pointer warping.");
207                 return 0;
208         }
209         return 1;
210 }
211
212 void tx_mouse::ungrab_linux_input() {
213         if (grab_mode == LINUX_INPUT) {
214                 // only remove the watch, we keep the channel as we dropped root and might fail to re-open it
215                 g_source_remove(linux_input_watch);
216         }
217 }
218
219 #define vtt vtt_class::focused_vtt
220
221 void tx_mouse::motion_notify(GtkWidget *widget, GdkEventMotion *eventMotion) {
222         if ((grab_mode == FALLBACK) && vtt) {
223                 gdouble d_x = eventMotion->x_root - x_abs;
224                 gdouble d_y = eventMotion->y_root - y_abs;
225
226                 if ((d_x != 0.0) || (d_y != 0.0)) {
227                         //gdouble xnow, ynow;
228                         //gdk_device_get_position_double(pointer, NULL, &xnow, &ynow);
229                         //printf("%lf -> %lf, %lf -> %lf\n", eventMotion->x_root, xnow, eventMotion->y_root, ynow);
230                         gdk_device_warp(pointer, screen, x_abs, y_abs);
231
232                         if (warp_override) {
233                                 f_prec value=(fabs(d_x)>fabs(d_y)) ? d_x : d_y;
234                                 vtt->sp_speed.handle_mouse_input(value*globals.mouse_speed*warp);
235                         } else {
236                                 vtt->xy_input((f_prec) d_x*warp, (f_prec) d_y*warp);
237                         }
238                 }
239         }
240 }
241
242 void tx_mouse::linux_input(tx_input_event *event) {
243         if ((grab_mode == LINUX_INPUT) && vtt) {
244                 if ((event->x_motion != 0) || (event->y_motion != 0)) {
245                         // gdk_device_warp(pointer, screen, x_abs, y_abs);
246                         gdouble d_x = event->x_motion;
247                         gdouble d_y = event->y_motion;
248
249                         if (warp_override) {
250                                 f_prec value=(fabs(d_x)>fabs(d_y)) ? d_x : d_y;
251                                 vtt->sp_speed.handle_mouse_input(value*globals.mouse_speed*warp);
252                         } else {
253                                 vtt->xy_input((f_prec) d_x*warp, (f_prec) d_y*warp);
254                         }
255                 }
256         }
257 }
258
259 void tx_mouse::button_press(GtkWidget *widget, GdkEventButton *eventButton) {
260         if (vtt) {
261                 switch(eventButton->button) {
262                         case 1: if (vtt->is_playing)
263                                         vtt->set_scratch(1);
264                                 else
265                                         vtt->sp_trigger.receive_input_value(1);
266                                 break;
267                         case 2: vtt->sp_mute.receive_input_value(1); break;
268                         case 3: vtt_class::focus_next(); break;
269                 }
270         }
271 }
272
273 void tx_mouse::button_release(GtkWidget *widget, GdkEventButton *eventButton) {
274         if (vtt) {
275                 switch (eventButton->button) {
276                         case 1: vtt->set_scratch(0); break;
277                         case 2: vtt->sp_mute.receive_input_value(0); break;
278                 }
279         }
280 }
281
282 void tx_mouse::key_press(GtkWidget *widget, GdkEventKey *eventKey) {
283         if (vtt) {
284                 switch(eventKey->keyval) {
285                         case GDK_KEY_space: if (press_key(KEY_space)) { vtt->set_scratch(1); } break;
286                         case GDK_KEY_Escape: if (press_key(KEY_Escape)) { ungrab(); } break;
287
288                         case GDK_KEY_Return: if (press_key(KEY_Return)) { vtt->sp_trigger.receive_input_value(1); } break;
289                         case GDK_KEY_BackSpace: if (press_key(KEY_BackSpace)) { vtt->sp_trigger.receive_input_value(0); } break;
290
291                         case GDK_KEY_Tab: if (press_key(KEY_Tab)) { vtt_class::focus_next(); } break;
292
293                         case GDK_KEY_s: if (press_key(KEY_s)) { vtt->sp_sync_client.receive_input_value(!vtt->is_sync_client); } break;
294
295                         case GDK_KEY_m:  if (press_key(KEY_m)) { vtt->sp_mute.receive_input_value(1); } break;
296                         case GDK_KEY_Control_L:  if (press_key(KEY_Control_L)) { vtt->sp_mute.receive_input_value(1); } break;
297                         case GDK_KEY_Control_R:  if (press_key(KEY_Control_R)) { vtt->sp_mute.receive_input_value(1); } break;
298
299                         case GDK_KEY_Alt_L:  if (press_key(KEY_Alt_L)) { vtt->sp_mute.receive_input_value(0); } break;
300                         case GDK_KEY_Alt_R:  if (press_key(KEY_Alt_R)) { vtt->sp_mute.receive_input_value(0); } break;
301
302                         case GDK_KEY_w:  if (press_key(KEY_w)) {
303                                 vtt->sp_mute.receive_input_value(1);
304                                 warp_override=true;
305                                 warp=((float) vtt->samples_in_buffer)/TX_MOUSE_SPEED_WARP;
306                                 vtt->set_scratch(1);
307                         }
308                         break;
309                         case GDK_KEY_f:  if (press_key(KEY_f)) {
310                                 warp_override=true;
311                                 warp=((float) vtt->samples_in_buffer)/TX_MOUSE_SPEED_WARP;
312                                 vtt->set_scratch(1);
313                         }
314                         break;
315
316                         case GDK_KEY_F1: if (press_key(KEY_F1)) { vtt_class::focus_no(0); } break;
317                         case GDK_KEY_F2: if (press_key(KEY_F2)) { vtt_class::focus_no(1); } break;
318                         case GDK_KEY_F3: if (press_key(KEY_F3)) { vtt_class::focus_no(2); } break;
319                         case GDK_KEY_F4: if (press_key(KEY_F4)) { vtt_class::focus_no(3); } break;
320                         case GDK_KEY_F5: if (press_key(KEY_F5)) { vtt_class::focus_no(4); } break;
321                         case GDK_KEY_F6: if (press_key(KEY_F6)) { vtt_class::focus_no(5); } break;
322                         case GDK_KEY_F7: if (press_key(KEY_F7)) { vtt_class::focus_no(6); } break;
323                         case GDK_KEY_F8: if (press_key(KEY_F8)) { vtt_class::focus_no(7); } break;
324                         case GDK_KEY_F9: if (press_key(KEY_F9)) { vtt_class::focus_no(8); } break;
325                         case GDK_KEY_F10: if (press_key(KEY_F10)) { vtt_class::focus_no(9); } break;
326                         case GDK_KEY_F11: if (press_key(KEY_F11)) { vtt_class::focus_no(10); } break;
327                         case GDK_KEY_F12: if (press_key(KEY_F12)) { vtt_class::focus_no(11); } break;
328                 }
329         }
330 }
331
332 void tx_mouse::key_release(GtkWidget *widget, GdkEventKey *eventKey) {
333         if (vtt) {
334                 switch(eventKey->keyval) {
335                         case GDK_KEY_space: if (release_key(KEY_space)) { vtt->set_scratch(0); } break;
336                         case GDK_KEY_Escape: release_key(KEY_Escape); break;
337
338                         case GDK_KEY_Return: release_key(KEY_Return); break;
339                         case GDK_KEY_BackSpace: release_key(KEY_BackSpace); break;
340
341                         case GDK_KEY_Tab: release_key(KEY_Tab); break;
342
343                         case GDK_KEY_s: release_key(KEY_s); break;
344
345
346                         case GDK_KEY_m: if (release_key(KEY_m)) { vtt->sp_mute.receive_input_value(0); } break;
347                         case GDK_KEY_Control_L: if (release_key(KEY_Control_L)) { vtt->sp_mute.receive_input_value(0); } break;
348                         case GDK_KEY_Control_R: if (release_key(KEY_Control_R)) { vtt->sp_mute.receive_input_value(0); } break;
349
350                         case GDK_KEY_Alt_L: if (release_key(KEY_Alt_L)) { vtt->sp_mute.receive_input_value(1); } break;
351                         case GDK_KEY_Alt_R: if (release_key(KEY_Alt_R)) { vtt->sp_mute.receive_input_value(1); } break;
352
353                         case GDK_KEY_w: if (release_key(KEY_w)) {
354                                 vtt->sp_mute.receive_input_value(0);
355                                 warp=TX_MOUSE_SPEED_NORMAL;
356                                 warp_override=false;
357                                 vtt->set_scratch(0);
358                         }
359                         break;
360
361                         case GDK_KEY_f: if (release_key(KEY_f)) {
362                                 warp=TX_MOUSE_SPEED_NORMAL;
363                                 warp_override=false;
364                                 vtt->set_scratch(0);
365                         }
366                         break;
367
368                         case GDK_KEY_F1: release_key(KEY_F1); break;
369                         case GDK_KEY_F2: release_key(KEY_F2); break;
370                         case GDK_KEY_F3: release_key(KEY_F3); break;
371                         case GDK_KEY_F4: release_key(KEY_F4); break;
372                         case GDK_KEY_F5: release_key(KEY_F5); break;
373                         case GDK_KEY_F6: release_key(KEY_F6); break;
374                         case GDK_KEY_F7: release_key(KEY_F7); break;
375                         case GDK_KEY_F8: release_key(KEY_F8); break;
376                         case GDK_KEY_F9: release_key(KEY_F9); break;
377                         case GDK_KEY_F10: release_key(KEY_F10); break;
378                         case GDK_KEY_F11: release_key(KEY_F11); break;
379                         case GDK_KEY_F12: release_key(KEY_F12); break;
380                 }
381         }
382 }
383
384 gboolean tx_mouse::motion_notify_wrap(GtkWidget *widget, GdkEventMotion *eventMotion, void *data) {
385         tx_mouse* mouse = (tx_mouse *) data;
386         if (mouse->grabbed) {
387                 mouse->motion_notify(widget, eventMotion);
388                 return TRUE;
389         } else {
390                 return FALSE;
391         }
392 }
393
394 gboolean tx_mouse::linux_input_wrap(GIOChannel *source, GIOCondition condition, gpointer data) {
395         if (condition == G_IO_IN) {
396                 tx_input_event sum;
397                 tx_input_event eventbuffer[512];
398                 tx_mouse* mouse = (tx_mouse *) data;
399                 gint fd = g_io_channel_unix_get_fd(mouse->linux_input_channel);
400                 ssize_t bytes_read = read(fd, &eventbuffer, sizeof(eventbuffer));
401
402                 //printf("read %lu bytes, %lu events\n", bytes_read, bytes_read / sizeof(tx_input_event));
403
404                 sum.x_motion = 0;
405                 sum.y_motion = 0;
406
407                 for (ssize_t i = 0; i < bytes_read / ((ssize_t) sizeof(tx_input_event)); i++) {
408                         sum.x_motion += eventbuffer[i].x_motion;
409                         sum.y_motion += eventbuffer[i].y_motion;
410                 }
411                 mouse->linux_input(&sum);
412         }
413         return TRUE;
414 }
415
416 gboolean tx_mouse::button_press_wrap(GtkWidget *widget, GdkEventButton *eventButton, void *data) {
417         tx_mouse* mouse = (tx_mouse *) data;
418         if (mouse->grabbed) {
419                 if (mouse->last_button_press != eventButton->time) {
420                         mouse->last_button_press = eventButton->time;
421
422                         tX_debug("tX_mouse::button-press-event (%u)", eventButton->button);
423                         mouse->button_press(widget, eventButton);
424                 } else {
425                         tX_debug("tX_mouse::button-press-event (%u) identical event skipped", eventButton->button);
426                 }
427                 return TRUE;
428         }
429         return FALSE;
430 }
431
432 gboolean tx_mouse::button_release_wrap(GtkWidget *widget, GdkEventButton *eventButton, void *data) {
433         tx_mouse* mouse = (tx_mouse *) data;
434         if (mouse->grabbed) {
435                 if (mouse->last_button_release != eventButton->time) {
436                         mouse->last_button_release = eventButton->time;
437
438                         tX_debug("tX_mouse::button-release-event (%u)", eventButton->button);
439                         mouse->button_release(widget, eventButton);
440                 } else {
441                         tX_debug("tX_mouse::button-release-event (%u) identical event skipped", eventButton->button);
442                 }
443
444                 return TRUE;
445         }
446
447         return FALSE;
448 }
449
450 gboolean tx_mouse::key_press_wrap(GtkWidget *widget, GdkEventKey *eventKey, void *data) {
451         tx_mouse* mouse = (tx_mouse *) data;
452         if (mouse->grabbed) {
453                 tX_debug("tX_mouse::key-press-event (%u)", eventKey->keyval);
454                 mouse->key_press(widget, eventKey);
455                 return TRUE;
456         } else {
457                 return FALSE;
458         }
459 }
460
461 gboolean tx_mouse::key_release_wrap(GtkWidget *widget, GdkEventKey *eventKey, void *data) {
462         tx_mouse* mouse = (tx_mouse *) data;
463         if (mouse->grabbed) {
464                 tX_debug("tX_mouse::key-release-event (%u)", eventKey->keyval);
465                 mouse->key_release(widget, eventKey);
466                 return TRUE;
467         } else {
468                 return FALSE;
469         }
470 }