commit 20fcde0

zzo38  ·  2023-04-15 00:33:12 +0000 UTC
parent 369f750
Implement joystick mode (currently untested; I do not have a joystick on my computer; someone who does have can test it)
2 files changed,  +169, -2
M main.c
+38, -0
 1@@ -10,6 +10,9 @@
 2 -I
 3   Use a separate thread to read from stdin.
 4 
 5+-J #
 6+  Set joystick mode. See the below section about joystick for details.
 7+
 8 -N
 9   Tells the window manager to omit the frame.
10 
11@@ -154,6 +157,41 @@ are being used, then this number may be set higher than 273.
12 (Currently there is not the setting of interpolation, filter, etc.)
13 
14 
15+=== Joystick ===
16+
17+The joystick parameter can be ? to list joysticks, or ? and a number to
18+display details about that joystick, or it can be the below specification
19+in order to configure joysticks.
20+
21+The first part is numbers with commas in between, being the joystick ID
22+numbers (as shown by the ? list); up to four numbers can be specified. If
23+a number is omitted, then that slot is not used.
24+
25+After the first part is a forward slash, and then the mapping (all
26+joysticks must use an identical mapping). Each item is a letter and then
27+a number, with no delimiters in between. The letters can be:
28+
29+A B S T U D L R
30+  Specify which joystick button to map to the uxn joypad button;
31+  respectively, A, B, SELECT, START, UP, DOWN, LEFT, RIGHT. Only the first
32+  128 buttons can be mapped.
33+
34+X Y x y
35+  Specify which axis to use as the X and Y axis. Uppercase letters means
36+  that axis is reversed; do not specify both "X" and "x", and do not
37+  specify both "Y" and "y".
38+
39+s
40+  Set sensitivity, from 1 to 32767. Higher numbers mean the joystick must
41+  be moved more in order to register, while lower numbers means that it
42+  does not need to moved much in order to be registered.
43+
44+H h I i
45+  Specify the hat number to use. Different letters correspond to different
46+  orientations (specify only up to one of these letters, not all of them).
47+  "h" is normal, "H" is reverse, "I" and "i" are sideways.
48+
49+
50 === Nonstandard features ===
51 
52 The -x switch enables the following nonstandard features, which include
M main.c
+131, -2
  1@@ -226,6 +226,13 @@ static SDL_Thread*in_thread;
  2 static Sint32*audio_option;
  3 static SDL_AudioSpec audiospec;
  4 
  5+static Uint8 joy_mask;
  6+static Uint8 joy_player[4];
  7+static Uint8 joy_hat,joy_hat_mode;
  8+static Uint8 joy_axis_x,joy_axis_y,joy_axis_mode;
  9+static Uint8 joy_button_map[128];
 10+static Uint16 joy_sensitivity=8192;
 11+
 12 static Uint8 debugger=0;
 13 static Uint16 breakpoint=0;
 14 static Uint16 break_pc=0x0100;
 15@@ -1140,6 +1147,14 @@ static void redraw(void) {
 16   SDL_Flip(screen);
 17 }
 18 
 19+static inline int select_joystick(Uint8 n) {
 20+  if((joy_mask&1) && n==joy_player[0]) return 2;
 21+  if((joy_mask&2) && n==joy_player[1]) return 5;
 22+  if((joy_mask&4) && n==joy_player[2]) return 6;
 23+  if((joy_mask&8) && n==joy_player[3]) return 7;
 24+  return 0;
 25+}
 26+
 27 static int run_screen(void) {
 28   SDL_Event e;
 29   int i,j,k;
 30@@ -1236,6 +1251,49 @@ static int run_screen(void) {
 31         device[1].d[2]=e.user.code;
 32         run(GET16(device[1].d));
 33         break;
 34+      case SDL_JOYAXISMOTION:
 35+        if(!joy_axis_mode) break;
 36+        if(j=select_joystick(e.jaxis.which)) {
 37+          k=device[8].d[j];
 38+          if(e.jaxis.axis==joy_axis_x) {
 39+            i=e.jaxis.value*(joy_axis_mode&1?-1:1);
 40+            k&=0x3F;
 41+            if(i>=joy_sensitivity) k|=0x80; else if(i<=-joy_sensitivity) k|=0x40;
 42+          } else if(e.jaxis.axis==joy_axis_y) {
 43+            i=e.jaxis.value*(joy_axis_mode&2?-1:1);
 44+            k&=0xCF;
 45+            if(i>=joy_sensitivity) k|=0x10; else if(i<=-joy_sensitivity) k|=0x20;
 46+          }
 47+          trigger:
 48+          if(k!=device[8].d[j]) {
 49+            device[8].d[j]=k;
 50+            if(!paused) run(GET16(device[8].d));
 51+          }
 52+        }
 53+        break;
 54+      case SDL_JOYBUTTONDOWN:
 55+      case SDL_JOYBUTTONUP:
 56+        if(e.jbutton.button&~127) break;
 57+        if(j=select_joystick(e.jbutton.which)) {
 58+          k=device[8].d[j];
 59+          i=joy_button_map[e.jbutton.button];
 60+          if(e.jbutton.state==SDL_PRESSED) k|=i; else k&=~i;
 61+          goto trigger;
 62+        }
 63+        break;
 64+      case SDL_JOYHATMOTION:
 65+        if(!joy_hat_mode) break;
 66+        if(j=select_joystick(e.jhat.which)) {
 67+          k=device[8].d[j]&0x0F;
 68+          // Note that the SDL_HAT_ values are bits counted clockwise from UP.
 69+          i=(e.jhat.value*0x1111)>>joy_hat_mode;
 70+          if(i&SDL_HAT_UP) k|=0x10;
 71+          if(i&SDL_HAT_DOWN) k|=0x20;
 72+          if(i&SDL_HAT_LEFT) k|=0x40;
 73+          if(i&SDL_HAT_RIGHT) k|=0x80;
 74+          goto trigger;
 75+        }
 76+        break;
 77     }
 78     if(picture_changed) redraw();
 79   }
 80@@ -1290,6 +1348,69 @@ static void set_audio(char*s) {
 81   if(!audio_option['e'-'a']) audio_option['e'-'a']=audio_option['r'-'a']/15;
 82 }
 83 
 84+static void set_joystick(char*s) {
 85+  int c,i,n;
 86+  SDL_Joystick*j;
 87+  if(*s=='?') {
 88+    if(SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_NOPARACHUTE)) errx(1,"Cannot initialize SDL: %s",SDL_GetError());
 89+    atexit(SDL_Quit);
 90+    if(s[1]) {
 91+      j=SDL_JoystickOpen(i=strtol(s+1,0,10));
 92+      if(!j) errx(1,"Cannot open joystick %d",i);
 93+      printf("Opened Joystick %d\n",i);
 94+      printf("Name: %s\n",SDL_JoystickName(i));
 95+      printf("Axes: %d\n",SDL_JoystickNumAxes(j));
 96+      printf("Buttons: %d\n",SDL_JoystickNumButtons(j));
 97+      printf("Hats: %d\n",SDL_JoystickNumHats(j));
 98+      printf("Balls: %d\n",SDL_JoystickNumBalls(j));
 99+      SDL_JoystickClose(j);
100+    } else {
101+      n=SDL_NumJoysticks();
102+      for(i=0;i<n;i++) printf("%d: %s\n",i,SDL_JoystickName(i));
103+    }
104+    exit(0);
105+    return;
106+  }
107+  n=0;
108+  while(*s) {
109+    if(*s>='0' && *s<='9') {
110+      if(n&~3) errx(1,"Too many players");
111+      joy_player[n]=strtol(s,&s,10);
112+      joy_mask|=1<<n;
113+    } else if(*s==',') {
114+      s++;
115+      n++;
116+    } else {
117+      break;
118+    }
119+  }
120+  if(*s=='/') ++s; else errx(1,"Improper joystick setting");
121+  while(*s) {
122+    c=*s++;
123+    n=strtol(s,&s,10);
124+    switch(c) {
125+      case 'A': joy_button_map[n&0x7F]|=0x01; break;
126+      case 'B': joy_button_map[n&0x7F]|=0x02; break;
127+      case 'D': joy_button_map[n&0x7F]|=0x20; break;
128+      case 'H': joy_hat=n; joy_hat_mode=6; break;
129+      case 'h': joy_hat=n; joy_hat_mode=4; break;
130+      case 'I': joy_hat=n; joy_hat_mode=5; break;
131+      case 'i': joy_hat=n; joy_hat_mode=7; break;
132+      case 'L': joy_button_map[n&0x7F]|=0x40; break;
133+      case 'R': joy_button_map[n&0x7F]|=0x80; break;
134+      case 'S': joy_button_map[n&0x7F]|=0x04; break;
135+      case 'T': joy_button_map[n&0x7F]|=0x08; break;
136+      case 'U': joy_button_map[n&0x7F]|=0x10; break;
137+      case 'X': joy_axis_x=n; joy_axis_mode|=1; break;
138+      case 'Y': joy_axis_y=n; joy_axis_mode|=2; break;
139+      case 's': joy_sensitivity=n; break;
140+      case 'x': joy_axis_x=n; joy_axis_mode|=4; break;
141+      case 'y': joy_axis_y=n; joy_axis_mode|=8; break;
142+      default: errx(1,"Improper joystick setting");
143+    }
144+  }
145+}
146+
147 int main(int argc,char**argv) {
148   int i,j;
149   for(i=0;i<16;i++) {
150@@ -1300,10 +1421,11 @@ int main(int argc,char**argv) {
151   device[10].aux=&uxnfile0;
152   device[11].aux=&uxnfile1;
153   device[12].in=datetime_in;
154-  while((i=getopt(argc,argv,"+ADFINQST:YZa:dh:ijnp:qs:t:w:xyz:"))>0) switch(i) {
155+  while((i=getopt(argc,argv,"+ADFIJ:NQST:YZa:dh:ijnp:qs:t:w:xyz:"))>0) switch(i) {
156     case 'D': scrflags|=SDL_DOUBLEBUF; break;
157     case 'F': scrflags|=SDL_FULLSCREEN; break;
158     case 'I': use_thread=1; break;
159+    case 'J': set_joystick(optarg); break;
160     case 'N': scrflags|=SDL_NOFRAME; break;
161     case 'Q': use_mouse=0; break;
162     case 'S': disallow_size_change=1; break;
163@@ -1332,7 +1454,7 @@ int main(int argc,char**argv) {
164   if(use_screen) {
165     if(zoom<1) errx(1,"Zoom out of range");
166     if(default_height<1 || default_width<1) errx(1,"Screen size out of range");
167-    if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|(audio_option?SDL_INIT_AUDIO:0))) errx(1,"Cannot initialize SDL: %s",SDL_GetError());
168+    if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|(audio_option?SDL_INIT_AUDIO:0)|(joy_mask?SDL_INIT_JOYSTICK:0))) errx(1,"Cannot initialize SDL: %s",SDL_GetError());
169     atexit(SDL_Quit);
170     device[2].out=screen_out;
171     scr_w=default_width;
172@@ -1340,6 +1462,13 @@ int main(int argc,char**argv) {
173     PUT16(device[2].d+2,scr_w);
174     PUT16(device[2].d+4,scr_h);
175     size_changed=1;
176+    if(joy_mask) {
177+      if((joy_mask&1) && !SDL_JoystickOpen(joy_player[0])) errx(1,"Cannot open joystick for player I");
178+      if((joy_mask&2) && !SDL_JoystickOpen(joy_player[1])) errx(1,"Cannot open joystick for player II");
179+      if((joy_mask&4) && !SDL_JoystickOpen(joy_player[2])) errx(1,"Cannot open joystick for player III");
180+      if((joy_mask&8) && !SDL_JoystickOpen(joy_player[3])) errx(1,"Cannot open joystick for player IV");
181+      SDL_JoystickEventState(SDL_ENABLE);
182+    }
183   }
184   if(use_console) device[1].out=console_out;
185   if(device[12].d[15]) device[12].d[15]=0; else device[12].in=datetime_in;