One of the things I was fiddling with was a driver for the Apple Graphics Tablet for the Apple 2, but didn't have a good input device for it.

So why not try to process input events for my wacom tablet device?

Code

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/uinput.h>
#include <stdlib.h>

#define msleep(ms) usleep((ms)*1000)

// Test to translate a tablet opened in exclusive mode into virtual button presses with uinput
// by Golden Child
// 
//
// using examples from
//
// https://gist.github.com/matthewaveryusa/a721aad80ae89a5c69f7c964fa20fec1
// https://github.com/GrantEdwards/uinput-joystick-demo
// https://www.kernel.org/doc/html/v4.12/input/uinput.html
// https://blog.marekkraus.sk/c/linuxs-uinput-usage-tutorial-virtual-gamepad/

// hard code the keyboard to open in keyboard_to_open
// you can pass the path of the keyboard to open on the command line

 char keyboard_to_open[256] = "/dev/input/by-id/usb-0461_USB_Wired_Keyboard-event-kbd";


static void setup_abs(int fd, unsigned chan, int min, int max);  

// demo showing how to provide a joystick/gamepad instance from
// userspace.

int main(int argc, char* argv[])
{ sleep(1); // wait for a second
  int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
  
  if (fd < 0)
    {
      perror("open /dev/uinput");
      return 1;
    }

  ioctl(fd, UI_SET_EVBIT, EV_KEY); // enable button/key handling

int translatetable[] = { 
BTN_TOOL_PEN, BTN_0,
//BTN_TOOL_RUBBER, BTN_1,  // don't seem used
//BTN_TOOL_BRUSH, BTN_2,
BTN_TOUCH, BTN_3,
BTN_STYLUS, BTN_4,
BTN_STYLUS2, BTN_5,
};

int translatetablelen = sizeof(translatetable) / sizeof (int);

int rcode; 


// tell uinput which BTN events we will generate
for (int i=0;i<translatetablelen;i+=2)     ioctl(fd, UI_SET_KEYBIT, translatetable[i+1]);

 char keyboard_name[256] = "Unknown";
 
 int keyboard_fd;

printf ("\nparameters %d\n",argc);
for (int i=0;i<argc;i++) printf("parameter %d = %s\n",i,argv[i]);
 if (argc > 1)
 	keyboard_fd = open(argv[1], O_RDONLY | O_NONBLOCK);  
else
    keyboard_fd = open(keyboard_to_open, O_RDONLY | O_NONBLOCK);  


  if ( keyboard_fd == -1 ) {
    printf("Failed to open keyboard.\n");
    exit(1);
  }
  rcode = ioctl(keyboard_fd, EVIOCGNAME(sizeof(keyboard_name)), keyboard_name);
  printf("Reading From : %s \n", keyboard_name);

  printf("Getting exclusive access: ");
  rcode = ioctl(keyboard_fd, EVIOCGRAB, 1);
  printf("%s\n", (rcode == 0) ? "SUCCESS" : "FAILURE");
 
for (int i=0;i<translatetablelen;i+=2)  printf("%d => %d\n",translatetable[i],translatetable[i+1]);

  printf("Press CTRL+C to exit\n");
  struct input_event tablet_event;


  ioctl(fd, UI_SET_EVBIT, EV_ABS); // enable analog absolute position handling
  
  setup_abs(fd, ABS_X,  -32767, 32767);
  setup_abs(fd, ABS_Y,  -32767, 32767);
  setup_abs(fd, ABS_PRESSURE,  -32767, 32767);
  setup_abs(fd, ABS_DISTANCE,  -32767, 32767);
  
  struct uinput_setup setup =
    {
     .name = "Userspace Joystick",
     .id =
     {
      .bustype = BUS_USB,
      .vendor  = 0x3,
      .product = 0x3,
      .version = 2,
     }
    };

  if (ioctl(fd, UI_DEV_SETUP, &setup))
    {
      perror("UI_DEV_SETUP");
      return 1;
    }
  
  if (ioctl(fd, UI_DEV_CREATE))
    {
      perror("UI_DEV_CREATE");
      return 1;
    }


int doquit=0; // must initialize variable or else it's nonzero
struct input_event button_event;
while (1) {      
      int readval;
      
      if ( readval = read(keyboard_fd, &tablet_event, sizeof(tablet_event)), readval != -1 ) 
        {
        if (tablet_event.code == KEY_ESC && tablet_event.type == EV_KEY) 
        {
        	doquit = 1;
        	printf("ESC pressed, exiting\n");
        }
      	if (tablet_event.type == EV_KEY) {
      		int found = 0;
      		for (int i=0;i<translatetablelen;i+=2) if (tablet_event.code == translatetable[i]) 
      					{
      					found = 1;
      					if (tablet_event.value != 2) {  // skip key repeat events
      					
						button_event.type = EV_KEY;
						button_event.code = translatetable[i+1];
						button_event.value = tablet_event.value;
						
						  printf("readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	tablet_event.code,
							button_event.code,
							button_event.value);
		
						  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
						  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
	
						  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						break;
						}    						
					}
					if (!found) printf ("TRANSLATE  UNKNOWN CODE %x\n",tablet_event.code);
				}
			else if  (tablet_event.type == EV_ABS) 		
					{
      					button_event.type = EV_ABS;
						button_event.code = tablet_event.code; 
						button_event.value = tablet_event.value;
						
						  printf("readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	tablet_event.code,
							button_event.code,
							button_event.value);
		
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
			    }
				
 	// if (readval <= 0) msleep(50); 
 	if (doquit) break;
    }
 	if (readval <= 0) msleep(50); // keep from taking a lot of cpu by going to sleep
}


  if(ioctl(fd, UI_DEV_DESTROY))
    {
      printf("UI_DEV_DESTROY");
      return 1;
    }

  close(fd);
  return 0;
}

// enable and configure an absolute "position" analog channel

static void setup_abs(int fd, unsigned chan, int min, int max)
{
  if (ioctl(fd, UI_SET_ABSBIT, chan))
    perror("UI_SET_ABSBIT");
  
  struct uinput_abs_setup s =
    {
     .code = chan,
     .absinfo = { .minimum = min,  .maximum = max },
    };

  if (ioctl(fd, UI_ABS_SETUP, &s))
    perror("UI_ABS_SETUP");
}



With the pen away from the tablet, all the axes and buttons report 0.

[Linked Image from i.imgur.com]

putting the stylus on the tablet,
button 0 is "proximity", button 1 is "actually pressing on tablet",
button 2 is the first stylus button and button 3 is the second stylus button.

Axis 0/1 is x,y and Axis 2/3 are distance, pressure

[Linked Image from i.imgur.com]



Code
sudo ./translatetablet /dev/input/by-id/usb-Wacom_Co._Ltd._CTL-460-event-mouse 

parameters 2
parameter 0 = ./translatetablet
parameter 1 = /dev/input/by-id/usb-Wacom_Co._Ltd._CTL-460-event-mouse
Reading From : Wacom Bamboo Pen Pen 
Getting exclusive access: SUCCESS
320 => 256
330 => 259
331 => 260
332 => 261
Press CTRL+C to exit
0xb0 readval=24  ev type=3  key in=0  => button out=0 value=8931
0xb0 readval=24  ev type=3  key in=1  => button out=1 value=178
0xb0 readval=24  ev type=3  key in=25  => button out=25 value=31
...