1 /**
2  * Keyboard and joystick
3 
4  * Copyright (C) 1997, 1998, 1999, 2002, 2003  Seth A. Robinson
5  * Copyright (C) 2005, 2007, 2008, 2009  Sylvain Beucler
6 
7  * This file is part of GNU FreeDink
8 
9  * GNU FreeDink is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 3 of the
12  * License, or (at your option) any later version.
13 
14  * GNU FreeDink is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18 
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see
21  * <http://www.gnu.org/licenses/>.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <string.h>
29 
30 #include "SDL.h"
31 #include "game_engine.h"
32 #include "log.h"
33 #include "input.h"
34 
35 /* Input state */
36 struct seth_joy sjoy;
37 
38 /* maps joystick buttons to action IDs (attack/attack/map/...). */
39 /* 10 buttons (indices), 6 different actions + 4 static buttons (values) */
40 static enum buttons_actions buttons_map[NB_BUTTONS];
41 
42 
43 /* TODO: maybe it's not necessary to SDL_PumpEvents every time, since
44    it's usually done before this function is called */
GetKeyboard(int key)45 int GetKeyboard(int key)
46 {
47   // returns 0 if the key has been depressed, else returns 1 and sets key to code recd.
48   int keystate_size;
49   Uint8* keystate;
50   //SDL_PumpEvents();
51 #if SDL_VERSION_ATLEAST(1, 3, 0)
52   keystate = SDL_GetKeyboardState(&keystate_size);
53   SDLKey scancode = SDL_GetScancodeFromKey(key);
54   return keystate[scancode];
55 #else
56   keystate = SDL_GetKeyState(&keystate_size);
57   return keystate[key];
58 #endif
59 }
60 
input_init(void)61 void input_init(void)
62 {
63   /* Enable Unicode to be able to grab what letter the user actually
64      typed, taking the keyboard layout/language into account. Used for
65      the console (game) and the input dialogs (editor). */
66   SDL_EnableUNICODE(1);
67 
68   /* Clear keyboard/joystick buffer */
69   memset(&sjoy,0,sizeof(sjoy));
70   {
71     int k = 0;
72     for (k = 0; k < 256; k++)
73       GetKeyboard(k);
74 
75     int a = ACTION_FIRST;
76     for (a = ACTION_FIRST; a < ACTION_LAST; a++)
77       sjoy.joybitold[a] = 0;
78   }
79   sjoy.rightold = 0;
80   sjoy.leftold  = 0;
81   sjoy.upold    = 0;
82   sjoy.downold  = 0;
83 
84   /* Define default button->action mapping */
85   input_set_default_buttons();
86 
87   /* JOY */
88   /* Joystick initialization never makes Dink fail for now. */
89   /* Note: joystick is originaly only used by the game, not the
90      editor. */
91   if (joystick == 1)
92     {
93       if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
94 	{
95 	  log_error("Error initializing joystick, skipping: %s", SDL_GetError());
96 	  joystick = 0;
97 	}
98       else
99 	{
100 	  /* first tests if a joystick driver is present */
101 	  /* if TRUE it makes certain that a joystick is plugged in */
102 	  if (SDL_NumJoysticks() > 0)
103 	    {
104 	      int i;
105 	      log_info("%i joysticks were found.", SDL_NumJoysticks());
106 	      log_info("The names of the joysticks are:");
107 	      for (i=0; i < SDL_NumJoysticks(); i++)
108 		log_info("    %s", SDL_JoystickName(i));
109 	      log_info("Picking the first one...");
110 	      jinfo = SDL_JoystickOpen(0);
111 	      /* Don't activate joystick events, Dink polls joystick
112 		 manually.  Plus events would pile up in the queue. */
113 	      SDL_JoystickEventState(SDL_IGNORE);
114 
115 	      if (jinfo) {
116 		log_info("Name: %s", SDL_JoystickName(0));
117 		log_info("Number of axes: %d", SDL_JoystickNumAxes(jinfo));
118 		log_info("Number of buttons: %d", SDL_JoystickNumButtons(jinfo));
119 		log_info("Number of balls: %d", SDL_JoystickNumBalls(jinfo));
120 		log_info("Number of hats: %d", SDL_JoystickNumHats(jinfo));
121 
122 		/* Flush stacked joystick events */
123 		{
124 		  SDL_Event event;
125 		  while (SDL_PollEvent(&event));
126 		}
127 
128 		joystick = 1;
129 	      } else {
130 		log_error("Couldn't open Joystick #0");
131 		joystick = 0;
132 	      }
133 	    }
134 	}
135     }
136 }
137 
input_quit(void)138 void input_quit(void)
139 {
140   if (joystick == 1)
141     {
142       if (jinfo != NULL)
143 	{
144 	  SDL_JoystickClose(jinfo);
145 	  jinfo = NULL;
146 	}
147       SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
148     }
149 }
150 
input_set_default_buttons(void)151 void input_set_default_buttons(void)
152 {
153   /* Set default button->action mapping */
154   int i = 0;
155   for (i = 0; i < NB_BUTTONS; i++)
156     input_set_button_action(i, ACTION_NOOP);
157 
158   input_set_button_action( 1-1, ACTION_ATTACK);
159   input_set_button_action( 2-1, ACTION_TALK);
160   input_set_button_action( 3-1, ACTION_MAGIC);
161   input_set_button_action( 4-1, ACTION_INVENTORY);
162   input_set_button_action( 5-1, ACTION_MENU);
163   input_set_button_action( 6-1, ACTION_MAP);
164   input_set_button_action( 7-1, ACTION_BUTTON7);
165   input_set_button_action( 8-1, ACTION_BUTTON8);
166   input_set_button_action( 9-1, ACTION_BUTTON9);
167   input_set_button_action(10-1, ACTION_BUTTON10);
168 
169 #ifdef _PSP
170   /* Alternate mapping, more consistent with other apps on PSP; in
171      addition, the buttons numbering/placement is different than on
172      PC, so it needs to be redefined anyway. */
173   /* Here are names for the button indices returned by SDL, 12 buttons
174      in [0,11]; home/hold/note/screen/vol+/vol- can't be used */
175   enum buttons_psp {
176     BUTTON_TRIANGLE=0, BUTTON_CIRCLE, BUTTON_CROSS, BUTTON_SQUARE,
177     BUTTON_LTRIGGER, BUTTON_RTRIGGER,
178     BUTTON_DOWN, BUTTON_LEFT, BUTTON_UP, BUTTON_RIGHT,
179     BUTTON_SELECT, BUTTON_START, BUTTON_HOME, BUTTON_HOLD };
180   input_set_button_action(BUTTON_TRIANGLE,  ACTION_INVENTORY);
181   input_set_button_action(BUTTON_CIRCLE,    ACTION_MAGIC);
182   input_set_button_action(BUTTON_CROSS,     ACTION_ATTACK);
183   input_set_button_action(BUTTON_SQUARE,    ACTION_TALK);
184   input_set_button_action(BUTTON_LTRIGGER,  ACTION_MENU);
185   input_set_button_action(BUTTON_RTRIGGER,  ACTION_MAP);
186   // TODO: make these also work like d/l/u/r:
187   input_set_button_action(BUTTON_DOWN,      ACTION_DOWN);
188   input_set_button_action(BUTTON_LEFT,      ACTION_LEFT);
189   input_set_button_action(BUTTON_UP,        ACTION_UP);
190   input_set_button_action(BUTTON_RIGHT,     ACTION_RIGHT);
191   /* TODO: we could also map:
192      - debug (Alt+D),
193      - pause/resume midi (Alt+N/B)
194      - fast-quit (Alt+Q) - fast-quit is somewhat already available
195      through the classic Home key, although handled differently. */
196   /* Let's also try to get a free key to possibly implement a
197      zooming/switch-view function for small screens, as well as a
198      virtual keyboard feature (like ScummVM)... */
199   /* Maybe also map inventory to start instead of Triangle. */
200   input_set_button_action(BUTTON_START,     ACTION_INVENTORY);
201 #endif
202 }
203 
input_get_button_action(int button_index)204 enum buttons_actions input_get_button_action(int button_index)
205 {
206   if (button_index >= 0 && button_index < NB_BUTTONS)
207     {
208       return buttons_map[button_index];
209     }
210   return -1; /* error */
211 }
212 
213 /**
214  * Set what action will be triggered when button 'button_index' is
215  * pressed. Action '0' currently means 'do nothing'.
216  */
input_set_button_action(int button_index,enum buttons_actions action_index)217 void input_set_button_action(int button_index, enum buttons_actions action_index)
218 {
219   if (button_index >= 0 && button_index < NB_BUTTONS)
220     {
221       if (action_index >= ACTION_FIRST && action_index < ACTION_LAST)
222 	buttons_map[button_index] = action_index;
223       else
224 	log_error("Attempted to set invalid action %d", action_index);
225     }
226   else
227     {
228       log_error("Attempted to set invalid button %d (internal index %d)",
229 		button_index+1, button_index);
230     }
231 }
232