whe.el

emacs support for the thrustmaster t-150 steering wheel
Log | Files | Refs | README

wheelmacs.cpp (5615B)


      1 #include <cstdint>
      2 #include <stdint.h>
      3 #include <thread>
      4 #include <memory>
      5 #include <vector>
      6 #include <cstdio>
      7 #include <cerrno>
      8 #include <atomic>
      9 #include <fcntl.h>
     10 #include <unistd.h>
     11 #include <iostream>
     12 #include <sys/socket.h>
     13 #include <netinet/in.h>
     14 #include <string.h>
     15 #include <sys/types.h>
     16 #include <arpa/inet.h>
     17 #include <mutex>
     18 
     19 #include "linux/joystick.h"
     20 #include "emacs-module.h"
     21 
     22 extern "C" {
     23 	#include "xdo.h"
     24     #include "wheelmesg.h"
     25 }
     26 
     27 #define VERSION 0.1
     28 int plugin_is_GPL_compatible;
     29 
     30 #define S(s) (env->intern(env, s)) 
     31 
     32 // Set the function cell of the symbol named NAME to SFUN using the 'fset' function.
     33 static void bind_function (emacs_env *env, const char *name, emacs_value Sfun) {
     34 	emacs_value Qfset = env->intern (env, "fset");
     35 	emacs_value Qsym = env->intern (env, name);
     36 	emacs_value args[] = { Qsym, Sfun };
     37 	env->funcall (env, Qfset, 2, args);
     38 }
     39 
     40 #define DEFUN(lsym, csym, amin, amax, doc, data)                        \
     41 	bind_function (env, lsym, env->make_function (env, amin, amax, csym, doc, data))
     42 
     43 static void provide (emacs_env *env, const char *feature) {
     44 	emacs_value Qfeat = env->intern (env, feature);
     45 	emacs_value Qprovide = env->intern (env, "provide");
     46 	emacs_value args[] = { Qfeat };
     47 
     48 	env->funcall (env, Qprovide, 1, args);
     49 }
     50 
     51 std::shared_ptr<std::vector<bool>> buttons;
     52 std::shared_ptr<std::vector<int16_t>> axes;
     53 std::thread worker;
     54 std::atomic_bool stop_worker;
     55 std::mutex cmd_mutex;
     56 struct wheel_cmd cmd;
     57 
     58 void grab_values() {
     59 	xdo_t* x = xdo_new(NULL);	
     60 	int fd = open("/dev/input/js0", O_RDONLY | O_NONBLOCK);
     61 
     62 	int s = socket(AF_INET, SOCK_DGRAM, 0);
     63 	struct sockaddr_in addr;
     64 	memset((char *)&addr, 0, sizeof(addr));
     65 	addr.sin_family = AF_INET; // Specify address family.
     66 	addr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY just 0.0.0.0, machine IP address
     67 	addr.sin_port = htons(8070); // Specify port.
     68 
     69     connect(s, (struct sockaddr *)&addr, sizeof(addr));
     70 	
     71 	while (true) {
     72 		struct js_event e;
     73 		int r = read(fd, &e, sizeof(e));
     74 		if (e.type & JS_EVENT_BUTTON) {
     75 			(*buttons)[e.number] = e.value ? true : false;
     76 			char key[4] = {0};
     77 			sprintf(key, "F%d", e.number+12);
     78 			if (e.value) xdo_send_keysequence_window(x, CURRENTWINDOW, key, 0);
     79 		} else if (e.type & JS_EVENT_AXIS) {
     80 			(*axes)[e.number] = e.value;
     81 		}
     82 		if (cmd.type != NONE) {
     83 			sendto(s, (void*)&cmd, sizeof(struct wheel_cmd), 0, NULL, sizeof(addr));
     84 			cmd.type = NONE;
     85 			cmd_mutex.unlock();
     86 		}
     87 		if (stop_worker) return;
     88 	}
     89 }
     90 
     91 static emacs_value wheel_init (emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept {
     92 	std::vector<bool> tmp_buttons(12);
     93 	std::vector<int16_t> tmp_axes(5);
     94 	std::fill(tmp_buttons.begin(), tmp_buttons.end(), false);
     95 	std::fill(tmp_axes.begin(), tmp_axes.end(), 0);	
     96 	buttons = std::make_unique<std::vector<bool>>(tmp_buttons);
     97 	axes = std::make_unique<std::vector<int16_t>>(tmp_axes);
     98 	
     99 	stop_worker = false;
    100 	worker = std::thread(grab_values);
    101 	return S("nil");
    102 }
    103 
    104 static emacs_value wheel_stop (emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept {	
    105 	stop_worker = true;
    106 	worker.join();
    107 	return S("nil");
    108 }
    109 
    110 static emacs_value wheel_set_autocenter (emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept {	
    111 	bool val = env->is_not_nil(env, args[0]);
    112 	// cmd_mutex.lock();
    113 	cmd = {
    114 		.type = AUTOCENTER,
    115 		.value = {.autocenter = val}
    116 	};
    117 	// cmd_mutex.unlock();
    118 	return S("nil");
    119 }
    120 
    121 static emacs_value wheel_set_range (emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept {	
    122 	int val = env->extract_integer(env, args[0]);
    123 	if (val < 270 || val > 1080) return S("nil");
    124 	cmd = {
    125 		.type = RANGE,
    126 		.value = {.range = val}
    127 	};
    128 	return S("nil");
    129 }
    130 
    131 static emacs_value wheel_set_gain (emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept {	
    132 	int val = env->extract_integer(env, args[0]);
    133 	if (val < 0 || val > 100) return S("nil");
    134 	cmd = {
    135 		.type = GAIN,
    136 		.value = {.gain = val}
    137 	};
    138 	return S("nil");
    139 }
    140 
    141 static emacs_value wheel_set_autocenter_force (emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept {
    142 	int val = env->extract_integer(env, args[0]);
    143 	if (val < 0 || val > 100) return S("nil");
    144 	cmd = {
    145 		.type = AUTOCENTER_FORCE,
    146 		.value = {.autocenter_force = val}
    147 	};
    148 	return S("nil");
    149 }
    150 
    151 static emacs_value wheel_button (emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept {
    152 	auto index = env->extract_integer(env, args[0]);
    153 	if (index >= (*buttons).size() || stop_worker) return S("nil");
    154 	bool down = (*buttons)[index];
    155 	return down ? S("t") : S("nil");
    156 }
    157 
    158 static emacs_value wheel_axis (emacs_env *env, ptrdiff_t nargs, emacs_value* args, void* data) noexcept {
    159 	auto index = env->extract_integer(env, args[0]);
    160 	if (index >= (*axes).size() || stop_worker) return S("nil");
    161 	int16_t val = (*axes)[index];
    162 	return env->make_integer(env, val);
    163 }
    164 
    165 extern int emacs_module_init (struct emacs_runtime *ert) noexcept {
    166 	emacs_env *env = ert->get_environment (ert);
    167 	DEFUN("wheel-init", wheel_init, 0, 0, "Set up whe.el worker thread", NULL);
    168 	DEFUN("wheel-stop", wheel_stop, 0, 0, "Stop whe.el worker thread", NULL);
    169 	DEFUN("wheel-button", wheel_button, 1, 1, "Check if button is pressed.", NULL);	
    170 	DEFUN("wheel-set-autocenter", wheel_set_autocenter, 1, 1, "Enable/disable autocentering.", NULL);
    171 	DEFUN("wheel-set-autocenter-force", wheel_set_autocenter_force, 1, 1, "", NULL);
    172 	DEFUN("wheel-set-range", wheel_set_range, 1, 1, "", NULL);
    173 	DEFUN("wheel-set-gain", wheel_set_gain, 1, 1, "", NULL);
    174 	DEFUN("wheel-axis", wheel_axis, 1, 1, "Get axis value.", NULL);	
    175 	provide(env, "wheel");
    176 	return 0;
    177 }