]> icculus.org git repositories - taylor/freespace2.git/blob - src/controlconfig/controlsconfig.cpp
Initial revision
[taylor/freespace2.git] / src / controlconfig / controlsconfig.cpp
1 /*
2  * $Logfile: /Freespace2/code/ControlConfig/ControlsConfig.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module for keyboard, joystick and mouse configuration
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:08  root
11  * Initial revision
12  *
13  * 
14  * 13    10/14/99 2:50p Jefff
15  * localization fixes
16  * 
17  * 12    8/16/99 9:50a Jefff
18  * fixed loading of tab bitmaps
19  * 
20  * 11    8/11/99 3:21p Jefff
21  * added tab highlights on conflict
22  * 
23  * 10    7/26/99 5:25p Jefff
24  * removed invalidation of key binding for demo builds
25  * 
26  * 9     7/19/99 2:13p Dave
27  * Added some new strings for Heiko.
28  * 
29  * 8     7/15/99 9:20a Andsager
30  * FS2_DEMO initial checkin
31  * 
32  * 7     6/19/99 2:46p Dave
33  * New control config screen.
34  * 
35  * 6     1/30/99 5:08p Dave
36  * More new hi-res stuff.Support for nice D3D textures.
37  * 
38  * 5     1/15/99 11:29a Neilk
39  * Fixed D3D screen/texture pixel formatting problem. 
40  * 
41  * 4     11/05/98 4:18p Dave
42  * First run nebula support. Beefed up localization a bit. Removed all
43  * conditional compiles for foreign versions. Modified mission file
44  * format.
45  * 
46  * 3     10/13/98 9:28a Dave
47  * Started neatening up freespace.h. Many variables renamed and
48  * reorganized. Added AlphaColors.[h,cpp]
49  * 
50  * 2     10/07/98 10:52a Dave
51  * Initial checkin.
52  * 
53  * 1     10/07/98 10:48a Dave
54  * 
55  * 61    8/09/98 11:55a Lawrance
56  * if GRAVIS_OEM is defined, map the throttle axis by default
57  * 
58  * 60    6/19/98 3:51p Lawrance
59  * localize control text
60  * 
61  * 59    6/17/98 11:04a Lawrance
62  * localize the control config strings
63  * 
64  * 58    6/13/98 5:19p Hoffoss
65  * externalized control config texts.
66  * 
67  * 57    6/09/98 5:15p Lawrance
68  * French/German localization
69  * 
70  * 56    6/09/98 10:31a Hoffoss
71  * Created index numbers for all xstr() references.  Any new xstr() stuff
72  * added from here on out should be added to the end if the list.  The
73  * current list count can be found in FreeSpace.cpp (search for
74  * XSTR_SIZE).
75  * 
76  * 55    6/01/98 11:43a John
77  * JAS & MK:  Classified all strings for localization.
78  * 
79  * 54    5/26/98 11:10a Lawrance
80  * Fix bug where window controls get disabled when F1 pressed twice
81  * 
82  * 53    5/20/98 10:35p Hoffoss
83  * Fixed bug with mouse buttons not working when action isn't continuous.
84  * 
85  * 52    5/19/98 4:08p Allender
86  * kill default binding for Z axis
87  * 
88  * 51    5/19/98 12:56p Hoffoss
89  * Added some code to help prevent triple-clicking for binding the mouse
90  * button to an action (why the hell people triple click is beyond me,
91  * though).
92  * 
93  * 50    5/19/98 11:11a Lawrance
94  * Ensure X and Y axis have defaults!
95  * 
96  * 49    5/18/98 4:53p Hoffoss
97  * Some force feedback tweaks and pilot initializations there should have
98  * been happening, but weren't, and not are!
99  * 
100  * 48    5/18/98 10:15a Lawrance
101  * Only do hud squad msg key check when necessary
102  * 
103  * 47    5/18/98 10:08a Lawrance
104  * deal with overlap between hud squad msg number keys and CC_CONTINUOUS
105  * keys
106  * 
107  * 46    5/17/98 5:44p Hoffoss
108  * Made throttle never bound by default (ask Sandeep why if interested).
109  * 
110  * 45    5/14/98 5:32p Hoffoss
111  * Improved axis binding code some more.
112  * 
113  * 44    5/13/98 7:15p Hoffoss
114  * Fixed remaining bugs with axis binding.
115  * 
116  * 43    5/13/98 1:17a Hoffoss
117  * Added joystick axes configurability.
118  * 
119  * 42    5/12/98 3:49p Hoffoss
120  * Fixed bug where double mouse click would bind mouse button right away.
121  * 
122  * 41    5/11/98 5:43p Hoffoss
123  * Made num lock not bindable.
124  * 
125  * 40    5/11/98 5:29p Hoffoss
126  * Added mouse button mapped to joystick button support.
127  * 
128  * 39    5/07/98 6:25p Dave
129  * Fix strange boundary conditions which arise when players die/respawn
130  * while the game is being ended. Spiff up the chatbox doskey thing a bit.
131  * 
132  * 38    5/05/98 1:48a Lawrance
133  * Add in missing help overlays
134  * 
135  * 37    4/27/98 10:11a Lawrance
136  * Add in disabled beep for missing buttons
137  * 
138  * 36    4/25/98 2:59p Hoffoss
139  * Fixed typo that was causing a bug.
140  * 
141  * 35    4/22/98 1:51a Lawrance
142  * Take out multiplayer key from demo key config
143  * 
144  * 34    4/16/98 4:29p Hoffoss
145  * Fixed bank_when_pressed functionality when using alt or shift for it.
146  * 
147  * 33    4/15/98 11:06a Lawrance
148  * fix bug with a multi key showing up in demo, remove obsolete bindings
149  * from demo and full version
150  * 
151  * 32    4/14/98 2:45p Hoffoss
152  * Made hitting escape to exit screen not play failed sound.
153  * 
154  * 31    4/14/98 2:27p Hoffoss
155  * Made certain actions be hidden in demo build.
156  * 
157  * 30    4/13/98 2:38p Hoffoss
158  * Added a tooltip handler and make binding attempts with illegal keys
159  * show a popup.
160  * 
161  * 29    4/11/98 7:59p Lawrance
162  * Add support for help overlays
163  * 
164  * 28    4/09/98 4:12p Hoffoss
165  * Changed check_control() to automatically register a control as used if
166  * it detects it being used.
167  * 
168  * 27    4/08/98 11:11a Hoffoss
169  * Fixed some bugs that showed up due to fixing other bugs the other day
170  * with controls.
171  * 
172  * 26    4/07/98 3:47p Hoffoss
173  * Fixed continuous controls checking with respect to modifiers.
174  * 
175  * 25    4/06/98 11:17a Hoffoss
176  * Fixed num lock/pause interplay bug.
177  * 
178  * 24    4/03/98 3:51p Hoffoss
179  * Fixed some bugs, and made changed Interplay requested regarding search
180  * mode.
181  * 
182  * 23    3/31/98 4:12p Hoffoss
183  * Made control used status clear at mission init time.
184  * 
185  * 22    3/23/98 11:28a Hoffoss
186  * Fixed flashing question mark bug.
187  * 
188  * 21    3/21/98 11:30a John
189  * Fixed bug where joymouse caused you to stay in binding mode when
190  * binding joystick button 1 to something.
191  * 
192  * 20    3/20/98 3:37p Hoffoss
193  * Tried to fix mitri's bug, failed miserably.
194  * 
195  * 19    3/19/98 5:04p Dave
196  * Put in support for targeted multiplayer text and voice messaging (all,
197  * friendly, hostile, individual).
198  * 
199  * 18    3/18/98 12:03p John
200  * Marked all the new strings as externalized or not.
201  * 
202  * 17    3/18/98 10:16a Hoffoss
203  * Fixed warning.
204  * 
205  * 16    3/17/98 11:15a Hoffoss
206  * Made question mark that appears when you are in bind mode flash.
207  * 
208  * 15    3/17/98 10:48a Hoffoss
209  * Allowed a special hack for "bank while pressed" action to use alt and
210  * shift keys standalone.
211  * 
212  * 14    3/12/98 3:22p Hoffoss
213  * Fixed 2 bugs with one solution!  Yay!  Failed sound on bind fixed and
214  * pad enter now not translated to enter.
215  * 
216  * 13    3/11/98 5:28p Hoffoss
217  * Added control config debug display info to possibly aid in tracking
218  * down a bug.
219  * 
220  * 12    2/26/98 10:07p Hoffoss
221  * Rewrote state saving and restoring to fix bugs and simplify the code.
222  * 
223  * 11    2/22/98 12:19p John
224  * Externalized some strings
225  * 
226  * 10    2/20/98 3:39p Hoffoss
227  * Updated code for new control config screen artwork.
228  * 
229  * 9     2/09/98 2:50p Hoffoss
230  * Made 'none' show up as gray instead of normal color, to distinguish it
231  * from actions with bound keys.
232  * 
233  * 8     2/07/98 10:04p Hoffoss
234  * Changed color and placement of "more" indicator.
235  * 
236  * 7     2/05/98 10:42a Hoffoss
237  * Fixed bug where while in bind mode, you could change the line selected
238  * using the mouse, and binding would work on the new line instead.
239  * 
240  * 6     2/03/98 5:05p Hoffoss
241  * Added "clear other" button to clear all conflicting bindings with
242  * selected action.
243  * 
244  * 5     1/22/98 4:53p Hoffoss
245  * Made training messages/directives display a joystick button in place of
246  * a keypress if there is no keypress bound to the action.
247  * 
248  * 4     1/20/98 4:20p Hoffoss
249  * Removed confusing behavior of clear button clearing out the other
250  * binding in a conflict.
251  * 
252  * 3     1/08/98 12:11p Hoffoss
253  * Changed Rudder axis to Roll axis, added new function we can use to
254  * check what joystick axes are valid with.
255  * 
256  * 2     12/24/97 3:37p Hoffoss
257  * Moved control config stuff to seperate library to Fred can access it as
258  * well.
259  * 
260  * 1     12/24/97 11:58a Hoffoss
261  * 
262  * 98    12/22/97 2:15p Hoffoss
263  * Fixed bug where joystick axis lines weren't being displayed.
264  * 
265  * 97    12/16/97 2:44p Hoffoss
266  * Added clear button to control config screen.
267  * 
268  * 96    12/12/97 3:07p Hoffoss
269  * Changed how deleting bindings work.  Each control of an action can be
270  * deleted independently or both at once.
271  * 
272  * 95    12/07/97 2:36p John
273  * Made warp out be Alt+J instead of J
274  * 
275  * 94    12/03/97 4:59p Hoffoss
276  * Added reset sound and change control config sounds around.
277  * 
278  * 93    12/03/97 4:16p Hoffoss
279  * Changed sound stuff used in interface screens for interface purposes.
280  *
281  * $NoKeywords: $
282  *
283 */
284
285 #include "freespace.h"
286 #include "controlsconfig.h"
287 #include "gamesequence.h"
288 #include "player.h"
289 #include "2d.h"
290 #include "hudsquadmsg.h"
291 #include "key.h"
292 #include "timer.h"
293 #include "math.h"
294 #include "mouse.h"
295 #include "ui.h"
296 #include "joy.h"
297 #include "bmpman.h"
298 #include "sound.h"
299 #include "gamesnd.h"
300 #include "missionscreencommon.h"
301 #include "font.h"
302 #include "hud.h"
303 #include "managepilot.h"
304 #include "multi_pmsg.h"
305 #include "contexthelp.h"
306 #include "popup.h"
307 #include "uidefs.h"
308 #include "multiutil.h"
309 #include "alphacolors.h"
310
311 #define NUM_SYSTEM_KEYS                 14
312 #define NUM_BUTTONS                             19
313 #define NUM_TABS                                        4
314
315 // coordinate indicies
316 #define CONTROL_X_COORD 0
317 #define CONTROL_Y_COORD 1
318 #define CONTROL_W_COORD 2
319 #define CONTROL_H_COORD 3
320
321 char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
322         "ControlConfig",                // GR_640
323         "2_ControlConfig"               // GR_1024
324 };
325
326 char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
327         "ControlConfig-m",              // GR_640
328         "2_ControlConfig-m"             // GR_1024
329 };
330
331 // control list area
332 int Control_list_coords[GR_NUM_RESOLUTIONS][4] = {
333         {
334                 32, 58, 198, 259                        // GR_640
335         },
336         {
337                 32, 94, 904, 424                        // GR_1024
338         }
339 };
340
341 // width of the control name section of the list
342 int Control_list_ctrl_w[GR_NUM_RESOLUTIONS] = {
343         350,            // GR_640
344         600             // GR_1024
345 };
346
347 // x start position of the binding area section of the list
348 int Control_list_key_x[GR_NUM_RESOLUTIONS] = {
349         397,            // GR_640
350         712             // GR_1024
351 };
352
353 // width of the binding area section of the list
354 int Control_list_key_w[GR_NUM_RESOLUTIONS] = {
355         198,            // GR_640
356         230             // GR_1024
357 };
358
359 // display the "more..." text under the control list
360 int Control_more_coords[GR_NUM_RESOLUTIONS][2] = {
361         {
362                 320, 326                        // GR_640
363         },
364         {
365                 500, 542                        // GR_1024
366         }
367 };
368
369 // area to display "conflicts with..." text
370 int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
371         {
372                 32, 313, 250, 32        // GR_640
373         },
374         {
375                 48, 508, 354, 46        // GR_1024
376         }
377 };
378
379 // conflict warning anim coords
380 int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = {
381         {
382                 -1, 420                 // GR_640
383         },
384         {
385                 -1, 669                 // GR_1024
386         }
387 };
388
389 // for flashing the conflict text
390 #define CONFLICT_FLASH_TIME     250
391 int Conflict_stamp = -1;
392 int Conflict_bright = 0;
393
394 #define LIST_BUTTONS_MAX        40
395 #define JOY_AXIS                                0x80000
396
397 static int Num_cc_lines;
398 static struct {
399         char *label;
400         int cc_index;  // index into Control_config of item
401         int y;  // Y coordinate of line
402         int kx, kw, jx, jw;  // x start and width of keyboard and joystick bound text
403 } Cc_lines[CCFG_MAX];
404
405 // struct to hold backup config_item elements so we can undo them
406 struct config_item_undo {
407         int size;
408         int *index;  // array (size) of Control_config indices of replaced elements
409         config_item *list;  // array (size) of original elements
410         config_item_undo *next;
411 };
412
413 config_item Control_config_backup[CCFG_MAX];
414
415 #ifdef GRAVIS_OEM
416 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
417 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
418 #else
419 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
420 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
421 #endif
422
423 // all this stuff is localized/externalized
424 #define NUM_AXIS_TEXT                   6
425 #define NUM_MOUSE_TEXT                  5
426 #define NUM_MOUSE_AXIS_TEXT     2
427 #define NUM_INVERT_TEXT                 2       
428 char *Joy_axis_action_text[NUM_JOY_AXIS_ACTIONS];
429 char *Joy_axis_text[NUM_AXIS_TEXT];
430 char *Mouse_button_text[NUM_MOUSE_TEXT];
431 char *Mouse_axis_text[NUM_MOUSE_AXIS_TEXT];
432 char *Invert_text[NUM_INVERT_TEXT];
433
434 ubyte System_keys[NUM_SYSTEM_KEYS] = {
435         KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,
436         KEY_F11, KEY_F12, KEY_PRINT_SCRN
437 };
438
439 int Control_check_count = 0;
440
441 static int Tab;  // which tab we are currently in
442 static int Binding_mode = 0;  // are we waiting for a key to bind it?
443 static int Bind_time = 0;
444 static int Search_mode = 0;  // are we waiting for a key to bind it?
445 static int Last_key = -1;
446 static int Selected_line = 0;  // line that is currently selected for binding
447 static int Selected_item = -1;  // -1 = none, 0 = key, 1 = button
448 static int Scroll_offset;
449 static int Axis_override = -1;
450 static int Background_bitmap;
451 static int Conflicts_tabs[NUM_TABS];
452 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX];  // buttons for each line of text in list
453 static UI_WINDOW Ui_window;
454
455 static struct {
456         int key;  // index of other control in conflict with this one
457         int joy;  // index of other control in conflict with this one
458 } Conflicts[CCFG_MAX];
459
460 int Conflicts_axes[NUM_JOY_AXIS_ACTIONS];
461
462 #define TARGET_TAB                              0
463 #define SHIP_TAB                                        1
464 #define WEAPON_TAB                              2
465 #define COMPUTER_TAB                            3
466 #define SCROLL_UP_BUTTON                4
467 #define SCROLL_DOWN_BUTTON              5
468 #define ALT_TOGGLE                              6
469 #define SHIFT_TOGGLE                            7
470 #define INVERT_AXIS                             8
471 #define CANCEL_BUTTON                   9
472 #define UNDO_BUTTON                             10
473 #define RESET_BUTTON                            11
474 #define SEARCH_MODE                             12
475 #define BIND_BUTTON                             13
476 #define HELP_BUTTON                             14
477 #define ACCEPT_BUTTON                   15
478 #define CLEAR_OTHER_BUTTON              16
479 #define CLEAR_ALL_BUTTON                17
480 #define CLEAR_BUTTON                            18
481
482 ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
483         { // GR_640
484                 ui_button_info("CCB_00",        32,     348,    17,     384,    0),     // target tab
485                 ui_button_info("CCB_01",        101,    348,    103,    384,    1),     // ship tab
486                 ui_button_info("CCB_02",        173,    352,    154,    384,    2),     // weapon tab
487                 ui_button_info("CCB_03",        242,    347,    244,    384,    3),     // computer/misc tab
488                 ui_button_info("CCB_04",        614,    73,     -1,     -1,     4),     // scroll up
489                 ui_button_info("CCB_05",        614,    296,    -1,     -1,     5),     // scroll down
490                 ui_button_info("CCB_06",        17,     452,    12,     440,    6),     // alt toggle
491                 ui_button_info("CCB_07",        56,     452,    50,     440,    7),     // shift toggle
492                 ui_button_info("CCB_09",        162,    452,    155,    440,    9),     // invert
493                 ui_button_info("CCB_10",        404,    1,              397,    45,     10),    // cancel
494                 ui_button_info("CCB_11",        582,    347,    586,    386,    11),    // undo
495                 ui_button_info("CCB_12",        576,    1,              578,    45,     12),    // default
496                 ui_button_info("CCB_13",        457,    4,              453,    45,     13),    // search
497                 ui_button_info("CCB_14",        516,    4,              519,    45,     14),    // bind
498                 ui_button_info("CCB_15",        540,    428,    500,    440,    15),    // help
499                 ui_button_info("CCB_16",        574,    432,    571,    412,    16),    // accept
500                 ui_button_info("CCB_18",        420,    346,    417,    386,    18),    // clear other 
501                 ui_button_info("CCB_19",        476,    346,    474,    386,    19),    // clear all
502                 ui_button_info("CCB_20",        524,    346,    529,    386,    20),    // clear button
503         },
504         { // GR_1024
505                 ui_button_info("2_CCB_00",      51,     557,    27,     615,    0),     // target tab
506                 ui_button_info("2_CCB_01",      162,    557,    166,    615,    1),     // ship tab
507                 ui_button_info("2_CCB_02",      277,    563,    246,    615,    2),     // weapon tab
508                 ui_button_info("2_CCB_03",      388,    555,    391,    615,    3),     // computer/misc tab
509                 ui_button_info("2_CCB_04",      982,    117,    -1,     -1,     4),     // scroll up
510                 ui_button_info("2_CCB_05",      982,    474,    -1,     -1,     5),     // scroll down
511                 ui_button_info("2_CCB_06",      28,     723,    24,     704,    6),     // alt toggle
512                 ui_button_info("2_CCB_07",      89,     723,    80,     704,    7),     // shift toggle
513                 ui_button_info("2_CCB_09",      260,    723,    249,    704,    9),     // invert
514                 ui_button_info("2_CCB_10",      646,    2,              635,    71,     10),    // cancel
515                 ui_button_info("2_CCB_11",      932,    555,    938,    619,    11),    // undo
516                 ui_button_info("2_CCB_12",      921,    1,              923,    71,     12),    // default
517                 ui_button_info("2_CCB_13",      732,    6,              726,    71,     13),    // search
518                 ui_button_info("2_CCB_14",      825,    6,              831,    71,     14),    // bind
519                 ui_button_info("2_CCB_15",      864,    685,    800,    704,    15),    // help
520                 ui_button_info("2_CCB_16",      919,    692,    914,    660,    16),    // accept
521                 ui_button_info("2_CCB_18",      672,    553,    668,    619,    18),    // clear other 
522                 ui_button_info("2_CCB_19",      761,    553,    749,    619,    19),    // clear all
523                 ui_button_info("2_CCB_20",      838,    553,    846,    619,    20),    // clear button
524         }
525 };
526
527 // strings
528 #define CC_NUM_TEXT             20
529 UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT] = {
530         { // GR_640
531                 { "Targeting",          1340,           17,     384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][TARGET_TAB].button },
532                 { "Ship",                       1341,           103,    384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIP_TAB].button },
533                 { "Weapons",            1065,           154,    384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][WEAPON_TAB].button },
534                 { "Misc",                       1411,           244,    384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][COMPUTER_TAB].button },         
535                 { "Alt",                                1510,           12,     440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][ALT_TOGGLE].button },
536                 { "Shift",                      1511,           50,     440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIFT_TOGGLE].button },
537                 { "Invert",                     1342,           155,    440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][INVERT_AXIS].button },
538                 { "Cancel",                     641,            397,    45,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CANCEL_BUTTON].button },
539                 { "Undo",                       1343,           586,    386,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][UNDO_BUTTON].button },
540                 { "Defaults",           1344,           568,    45,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][RESET_BUTTON].button },
541                 { "Search",                     1345,           453,    45,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SEARCH_MODE].button },
542                 { "Bind",                       1346,           519,    45,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][BIND_BUTTON].button },
543                 { "Help",                       928,            500,    440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][HELP_BUTTON].button },
544                 { "Accept",                     1035,           571,    412,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][ACCEPT_BUTTON].button },
545                 { "Clear",                      1347,           417,    386,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
546                 { "Conflict",           1348,           406,    396,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
547                 { "Clear",                      1413,           474,    386,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
548                 { "All",                                1349,           483,    396,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
549                 { "Clear",                      1414,           529,    388,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
550                 { "Selected",           1350,           517,    396,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
551         },
552         { // GR_1024
553                 { "Targeting",          1340,           47,     615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][TARGET_TAB].button },
554                 { "Ship",                       1341,           176,    615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIP_TAB].button },
555                 { "Weapons",            1065,           266,    615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][WEAPON_TAB].button },
556                 { "Misc",                       1411,           401,    615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][COMPUTER_TAB].button },         
557                 { "Alt",                                1510,           29,     704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][ALT_TOGGLE].button },
558                 { "Shift",                      1511,           85,     704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIFT_TOGGLE].button },
559                 { "Invert",                     1342,           254,    704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][INVERT_AXIS].button },
560                 { "Cancel",                     641,            655,    71,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CANCEL_BUTTON].button },
561                 { "Undo",                       1343,           938,    619,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][UNDO_BUTTON].button },
562                 { "Defaults",           1344,           923,    71,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][RESET_BUTTON].button },
563                 { "Search",                     1345,           746,    71,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SEARCH_MODE].button },
564                 { "Bind",                       1346,           846,    71,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][BIND_BUTTON].button },
565                 { "Help",                       928,            800,    704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][HELP_BUTTON].button },
566                 { "Accept",                     1035,           914,    660,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][ACCEPT_BUTTON].button },
567                 { "Clear",                      1347,           683,    619,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
568                 { "Conflict",           1348,           666,    634,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
569                 { "Clear",                      1413,           759,    619,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
570                 { "All",                                1349,           772,    634,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
571                 { "Clear",                      1414,           871,    619,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
572                 { "Selected",           1350,           852,    634,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
573         }
574 };
575
576 // linked list head of undo items
577 config_item_undo *Config_item_undo;
578
579 // same indices as Scan_code_text[].  Indicates if a scancode is allowed to be bound.
580 int Config_allowed[] = {
581         0, 0, 1, 1, 1, 1, 1, 1,
582         1, 1, 1, 1, 1, 1, 1, 1,
583         1, 1, 1, 1, 1, 1, 1, 1,
584         1, 1, 1, 1, 1, 1, 1, 1,
585
586         1, 1, 1, 1, 1, 1, 1, 1,
587         1, 0, 1, 1, 1, 1, 1, 1,
588         1, 1, 1, 1, 1, 1, 1, 1,
589         1, 1, 1, 0, 0, 0, 0, 0,
590
591         0, 0, 0, 0, 0, 0, 1, 1,
592         1, 1, 1, 1, 1, 1, 1, 1,
593         1, 1, 1, 1, 0, 0, 0, 0,
594         0, 0, 0, 0, 0, 0, 0, 0,
595
596         0, 0, 0, 0, 0, 0, 0, 0,
597         0, 0, 0, 0, 0, 0, 0, 0,
598         0, 0, 0, 0, 0, 0, 0, 0,
599         0, 0, 0, 0, 0, 0, 0, 0,
600
601         0, 0, 0, 0, 0, 0, 0, 0,
602         0, 0, 0, 0, 0, 0, 0, 0,
603         0, 0, 0, 0, 0, 0, 0, 0,
604         0, 0, 0, 0, 1, 1, 0, 0,
605
606         0, 0, 0, 0, 0, 0, 0, 0,
607         0, 0, 0, 0, 0, 0, 0, 0,
608         0, 0, 0, 0, 0, 1, 0, 0,
609         1, 0, 0, 0, 0, 0, 0, 0,
610
611         0, 0, 0, 0, 0, 0, 0, 1,
612         1, 1, 0, 1, 0, 1, 0, 1,
613         1, 1, 1, 1, 0, 0, 0, 0,
614         0, 0, 0, 0, 0, 0, 0, 0,
615
616         0, 0, 0, 0, 0, 0, 0, 0,
617         0, 0, 0, 0, 0, 0, 0, 0,
618         0, 0, 0, 0, 0, 0, 0, 0,
619         0, 0, 0, 0, 0, 0, 0, 0,
620 };
621
622
623 /*
624 // old invalid demo keys
625 #define INVALID_DEMO_KEYS_MAX   14
626 int Invalid_demo_keys[] = {
627         INCREASE_SHIELD,
628         DECREASE_SHIELD,
629         SHIELD_EQUALIZE,
630         SHIELD_XFER_TOP,
631         SHIELD_XFER_BOTTOM,
632         SHIELD_XFER_LEFT,
633         SHIELD_XFER_RIGHT,
634         XFER_SHIELD,
635         XFER_LASER,
636         MULTI_MESSAGE_ALL,
637         MULTI_MESSAGE_FRIENDLY,
638         MULTI_MESSAGE_HOSTILE,
639         MULTI_MESSAGE_TARGET,
640         MULTI_OBSERVER_ZOOM_TO
641 };
642 */
643 #define INVALID_DEMO_KEYS_MAX   0
644 int Invalid_demo_keys[INVALID_DEMO_KEYS_MAX+1];         // +1 is only to prevent a 0-size array;
645
646
647 #ifndef NDEBUG
648 int Show_controls_info = 0;
649
650 DCF_BOOL(show_controls_info, Show_controls_info)
651 #endif
652
653 static int Axes_origin[JOY_NUM_AXES];
654
655 void control_config_detect_axis_reset()
656 {
657         joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin);
658 }
659
660 int control_config_detect_axis()
661 {
662         int i, d, axis = -1, delta = 16384;
663         int axes_values[JOY_NUM_AXES];
664
665         joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
666         for (i=0; i<JOY_NUM_AXES; i++) {
667                 d = abs(axes_values[i] - Axes_origin[i]);
668                 if (d > delta) {
669                         axis = i;
670                         delta = d;
671                 }
672         }
673
674         return axis;
675 }
676
677 int control_config_valid_action(int n)
678 {
679 #ifdef FS2_DEMO
680         int i;
681
682         for (i=0; i<INVALID_DEMO_KEYS_MAX; i++)
683                 if (n == Invalid_demo_keys[i])
684                         return 0;
685 #endif
686
687         return 1;
688 }
689
690 void control_config_conflict_check()
691 {
692         int i, j, a, b, c, shift = -1, alt = -1;
693
694         for (i=0; i<CCFG_MAX; i++) {
695                 Conflicts[i].key = Conflicts[i].joy = -1;
696                 switch (Control_config[i].key_id) {
697                         case KEY_LSHIFT:
698                         case KEY_RSHIFT:
699                                 shift = i;
700                                 break;
701
702                         case KEY_LALT:
703                         case KEY_RALT:
704                                 alt = i;
705                                 break;
706                 }
707         }
708
709         for (i=0; i<NUM_TABS; i++)
710                 Conflicts_tabs[i] = 0;
711
712         for (i=0; i<CCFG_MAX-1; i++) {
713                 if (control_config_valid_action(i)) {
714                         for (j=i+1; j<CCFG_MAX; j++) {
715                                 if (control_config_valid_action(j)) {
716                                         if (Control_config[i].key_id >= 0) {
717                                                 c = 0;
718                                                 a = Control_config[i].key_id;
719                                                 b = Control_config[j].key_id;
720                                                 if (a == b) {
721                                                         Conflicts[i].key = j;
722                                                         Conflicts[j].key = i;
723                                                         Conflicts_tabs[ Control_config[i].tab ] = 1;
724                                                         Conflicts_tabs[ Control_config[j].tab ] = 1;
725                                                 }
726
727                 /*                              if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) {
728                                                         Conflicts[i].key = shift;
729                                                         Conflicts[shift].key = i;
730                                                         Conflicts_tabs[ Control_config[i].tab ] = 1;
731                                                         Conflicts_tabs[ Control_config[shift].tab ] = 1;
732                                                 }
733
734                                                 if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) {
735                                                         Conflicts[j].key = shift;
736                                                         Conflicts[shift].key = j;
737                                                         Conflicts_tabs[ Control_config[j].tab ] = 1;
738                                                         Conflicts_tabs[ Control_config[shift].tab ] = 1;
739                                                 }
740
741                                                 if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) {
742                                                         Conflicts[i].key = alt;
743                                                         Conflicts[alt].key = i;
744                                                         Conflicts_tabs[ Control_config[i].tab ] = 1;
745                                                         Conflicts_tabs[ Control_config[alt].tab ] = 1;
746                                                 }
747
748                                                 if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) {
749                                                         Conflicts[j].key = alt;
750                                                         Conflicts[alt].key = j;
751                                                         Conflicts_tabs[ Control_config[j].tab ] = 1;
752                                                         Conflicts_tabs[ Control_config[alt].tab ] = 1;
753                                                 }*/
754                                         }
755
756                                         if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
757                                                 Conflicts[i].joy = j;
758                                                 Conflicts[j].joy = i;
759                                                 Conflicts_tabs[ Control_config[i].tab ] = 1;
760                                                 Conflicts_tabs[ Control_config[j].tab ] = 1;
761                                         }
762                                 }
763                         }
764                 }
765         }
766
767         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
768                 Conflicts_axes[i] = -1;
769
770         for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
771                 for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
772                         if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
773                                 Conflicts_axes[i] = j;
774                                 Conflicts_axes[j] = i;
775                                 Conflicts_tabs[SHIP_TAB] = 1;
776                         }
777                 }
778         }
779 }
780
781 // do list setup required prior to rendering and checking for the controls listing.  Called when list changes
782 void control_config_list_prepare()
783 {
784         int j, k, y, z;
785         int font_height = gr_get_font_height();
786
787         Num_cc_lines = y = z = 0;
788         while (z < CCFG_MAX) {
789                 if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) {
790                         k = Control_config[z].key_id;
791                         j = Control_config[z].joy_id;
792                         Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z);
793                         Cc_lines[Num_cc_lines].cc_index = z;
794                         Cc_lines[Num_cc_lines++].y = y;
795                         y += font_height + 2;
796                 }
797
798                 z++;
799         }
800
801         if (Tab == SHIP_TAB) {
802                 for (j=0; j<NUM_JOY_AXIS_ACTIONS; j++) {
803                         Cc_lines[Num_cc_lines].label = Joy_axis_action_text[j];
804                         Cc_lines[Num_cc_lines].cc_index = j | JOY_AXIS;
805                         Cc_lines[Num_cc_lines++].y = y;
806                         y += font_height + 2;
807                 }
808         }
809 }
810
811 int cc_line_query_visible(int n)
812 {
813         int y;
814
815         if ((n < 0) || (n >= Num_cc_lines))
816                 return 0;
817         
818         y = Cc_lines[n].y - Cc_lines[Scroll_offset].y;
819         if ((y < 0) || (y + gr_get_font_height() > Control_list_coords[gr_screen.res][CONTROL_H_COORD])){
820                 return 0;
821         }
822
823         return 1;
824 }
825
826 // allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack).
827 // Returns a pointer to this newly allocated block
828 config_item_undo *get_undo_block(int size)
829 {
830         config_item_undo *ptr;
831
832         ptr = (config_item_undo *) malloc( sizeof(config_item_undo) );
833         Assert(ptr);
834         ptr->next = Config_item_undo;
835         Config_item_undo = ptr;
836
837         ptr->size = size;
838         if (size) {
839                 ptr->index = (int *) malloc( sizeof(int) * size );
840                 Assert(ptr->index);
841                 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
842                 Assert(ptr->list);
843
844         } else {
845                 ptr->index = NULL;
846                 ptr->list = NULL;
847         }
848
849         return ptr;
850 }
851
852 // frees one undo block.  The first one in the list (top of the stack) to be precise.
853 void free_undo_block()
854 {
855         config_item_undo *ptr;
856
857         ptr = Config_item_undo;
858         if (!ptr)
859                 return;
860
861         Config_item_undo = ptr->next;
862         if (ptr->size) {
863                 free(ptr->list);
864                 free(ptr->index);
865         }
866
867         free(ptr);
868 }
869
870 // undo the most recent binding changes
871 int control_config_undo_last()
872 {
873         int i, z, tab;
874
875         if (!Config_item_undo) {
876                 gamesnd_play_iface(SND_GENERAL_FAIL);
877                 return -1;
878         }
879
880         if (Config_item_undo->index[0] & JOY_AXIS)
881                 tab = SHIP_TAB;
882         else
883                 tab = Control_config[Config_item_undo->index[0]].tab;
884
885         for (i=1; i<Config_item_undo->size; i++) {
886                 if (Config_item_undo->index[i] & JOY_AXIS) {
887                         if (tab != SHIP_TAB)
888                                 tab = -1;
889
890                 } else {
891                         if (Control_config[Config_item_undo->index[i]].tab != tab)
892                                 tab = -1;
893                 }
894         }
895
896         if (tab >= 0)
897                 Tab = tab;
898
899         for (i=0; i<Config_item_undo->size; i++) {
900                 z = Config_item_undo->index[i];
901                 if (z & JOY_AXIS) {
902                         config_item *ptr;
903
904                         z &= ~JOY_AXIS;
905                         ptr = &Config_item_undo->list[i];
906                         Axis_map_to[z] = ptr->joy_id;
907                         Invert_axis[z] = ptr->used;
908
909                 } else {
910                         Control_config[z] = Config_item_undo->list[i];
911                 }
912         }
913
914         free_undo_block();
915         control_config_conflict_check();
916         control_config_list_prepare();
917         gamesnd_play_iface(SND_USER_SELECT);
918         return 0;
919 }
920
921 void control_config_save_axis_undo(int axis)
922 {
923         config_item_undo *ptr;
924         config_item item;
925
926         item.joy_id = (short) Axis_map_to[axis];
927         item.used = 0;
928         item.used = Invert_axis[axis];
929
930         ptr = get_undo_block(1);
931         ptr->index[0] = axis | JOY_AXIS;
932         ptr->list[0] = item;
933 }
934
935 void control_config_bind_key(int i, int key)
936 {
937         config_item_undo *ptr;
938
939         ptr = get_undo_block(1);
940         ptr->index[0] = i;
941         ptr->list[0] = Control_config[i];
942         Control_config[i].key_id = (short) key;
943 }
944
945 void control_config_bind_joy(int i, int joy)
946 {
947         config_item_undo *ptr;
948
949         ptr = get_undo_block(1);
950         ptr->index[0] = i;
951         ptr->list[0] = Control_config[i];
952         Control_config[i].joy_id = (short) joy;
953 }
954
955 void control_config_bind_axis(int i, int axis)
956 {
957         control_config_save_axis_undo(i);
958         Axis_map_to[i] = axis;
959 }
960
961 int control_config_remove_binding()
962 {
963         int z, j, k;
964         config_item_undo *ptr;
965
966         if (Selected_line < 0) {
967                 gamesnd_play_iface(SND_GENERAL_FAIL);
968                 return -1;
969         }
970
971         z = Cc_lines[Selected_line].cc_index;
972         if (z & JOY_AXIS) {
973                 z &= ~JOY_AXIS;
974                 if (Axis_map_to[z] < 0) {
975                         gamesnd_play_iface(SND_GENERAL_FAIL);
976                         return -1;
977                 }
978
979                 control_config_save_axis_undo(z);
980                 Axis_map_to[z] = -1;
981                 control_config_conflict_check();
982                 control_config_list_prepare();
983                 gamesnd_play_iface(SND_USER_SELECT);
984                 Selected_item = -1;
985                 return 0;
986         }
987
988         if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
989                 gamesnd_play_iface(SND_GENERAL_FAIL);
990                 return -1;
991         }
992
993         j = k = -1;
994         ptr = get_undo_block(1);
995         ptr->index[0] = z;
996         ptr->list[0] = Control_config[z];
997
998         if (Selected_item && (Control_config[z].joy_id >= 0))  // if not just key selected (which would be 0)
999                 Control_config[z].joy_id = (short) -1;
1000
1001         if ((Selected_item != 1) && (Control_config[z].key_id >= 0))  // if not just joy button selected (1)
1002                 Control_config[z].key_id = (short) -1;
1003
1004         control_config_conflict_check();
1005         control_config_list_prepare();
1006         gamesnd_play_iface(SND_USER_SELECT);
1007         Selected_item = -1;
1008         return 0;
1009 }
1010
1011 int control_config_clear_other()
1012 {
1013         int z, i, j, total = 0;
1014         config_item_undo *ptr;
1015
1016         if (Selected_line < 0) {
1017                 gamesnd_play_iface(SND_GENERAL_FAIL);
1018                 return -1;
1019         }
1020
1021         z = Cc_lines[Selected_line].cc_index;
1022         if (z & JOY_AXIS) {
1023                 config_item item;
1024
1025                 z &= ~JOY_AXIS;
1026                 if (Axis_map_to[z] < 0) {
1027                         gamesnd_play_iface(SND_GENERAL_FAIL);
1028                         return -1;
1029                 }
1030
1031                 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1032                         if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1033                                 total++;
1034
1035                 if (!total) {
1036                         gamesnd_play_iface(SND_GENERAL_FAIL);
1037                         return -1;
1038                 }
1039
1040                 ptr = get_undo_block(total);
1041                 for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1042                         if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
1043                                 item.joy_id = (short) Axis_map_to[i];
1044                                 item.used = Invert_axis[i];
1045
1046                                 ptr->index[j] = i | JOY_AXIS;
1047                                 ptr->list[j] = item;
1048                                 j++;
1049
1050                                 Axis_map_to[i] = -1;
1051                         }
1052
1053                 control_config_conflict_check();
1054                 control_config_list_prepare();
1055                 gamesnd_play_iface(SND_USER_SELECT);
1056                 return 0;
1057         }
1058
1059         for (i=0; i<CCFG_MAX; i++)
1060                 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1061                         if (i != z)
1062                                 total++;
1063
1064         if (!total) {
1065                 gamesnd_play_iface(SND_GENERAL_FAIL);
1066                 return -1;
1067         }
1068
1069         if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1070                 gamesnd_play_iface(SND_GENERAL_FAIL);
1071                 return -1;
1072         }
1073
1074         // now, back up the old bindings so we can undo if we want to
1075         ptr = get_undo_block(total);
1076         for (i=j=0; i<CCFG_MAX; i++)
1077                 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1078                         if (i != z) {
1079                                 ptr->index[j] = i;
1080                                 ptr->list[j] = Control_config[i];
1081                                 j++;
1082
1083                                 if (Control_config[i].key_id == Control_config[z].key_id)
1084                                         Control_config[i].key_id = (short) -1;
1085                                 if (Control_config[i].joy_id == Control_config[z].joy_id)
1086                                         Control_config[i].joy_id = (short) -1;
1087                         }
1088
1089         control_config_conflict_check();
1090         control_config_list_prepare();
1091         gamesnd_play_iface(SND_USER_SELECT);
1092         return 0;
1093 }
1094
1095 int control_config_clear_all()
1096 {
1097         int i, j, total = 0;
1098         config_item_undo *ptr;
1099
1100         // first, determine how many bindings need to be changed
1101         for (i=0; i<CCFG_MAX; i++)
1102                 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0))
1103                         total++;
1104
1105         if (!total) {
1106                 gamesnd_play_iface(SND_GENERAL_FAIL);
1107                 return -1;
1108         }
1109
1110         // now, back up the old bindings so we can undo if we want to
1111         ptr = get_undo_block(total);
1112         for (i=j=0; i<CCFG_MAX; i++) {
1113                 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
1114                         ptr->index[j] = i;
1115                         ptr->list[j] = Control_config[i];
1116                         j++;
1117                 }
1118         }
1119
1120         Assert(j == total);
1121         for (i=0; i<CCFG_MAX; i++) {
1122                 Control_config[i].key_id = Control_config[i].joy_id = -1;
1123         }
1124
1125         control_config_conflict_check();
1126         control_config_list_prepare();
1127         gamesnd_play_iface(SND_RESET_PRESSED);
1128         return 0;
1129 }
1130
1131 extern Joy_info joystick;
1132
1133 int control_config_axis_default(int axis)
1134 {
1135         Assert(axis >= 0);
1136
1137         if ( axis > 1 ) {
1138                 if (Axis_map_to_defaults[axis] < 0)
1139                         return -1;
1140
1141                 if (!joystick.axis_valid[Axis_map_to_defaults[axis]])
1142                         return -1;
1143         }
1144
1145         return Axis_map_to_defaults[axis];
1146 }
1147
1148 int control_config_do_reset()
1149 {
1150         int i, j, total = 0;
1151         config_item_undo *ptr;
1152         config_item item;
1153
1154         // first, determine how many bindings need to be changed
1155         for (i=0; i<CCFG_MAX; i++)
1156                 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1157                         total++;
1158
1159         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1160                 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1161                         total++;
1162
1163         if (!total) {
1164                 gamesnd_play_iface(SND_GENERAL_FAIL);
1165                 return -1;
1166         }
1167
1168         // now, back up the old bindings so we can undo if we want to
1169         ptr = get_undo_block(total);
1170         for (i=j=0; i<CCFG_MAX; i++) {
1171                 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1172                         ptr->index[j] = i;
1173                         ptr->list[j] = Control_config[i];
1174                         j++;
1175                 }
1176         }
1177
1178         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1179                 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1180                         item.joy_id = (short) Axis_map_to[i];
1181                         item.used = Invert_axis[i];
1182
1183                         ptr->index[j] = i | JOY_AXIS;
1184                         ptr->list[j] = item;
1185                         j++;
1186                 }
1187
1188         Assert(j == total);
1189         control_config_reset_defaults();
1190         control_config_conflict_check();
1191         control_config_list_prepare();
1192         gamesnd_play_iface(SND_RESET_PRESSED);
1193         return 0;
1194 }
1195
1196 // This sets all the controls to their default values
1197 void control_config_reset_defaults()
1198 {
1199         int i;
1200
1201         // Reset keyboard defaults
1202         for (i=0; i<CCFG_MAX; i++) {
1203                 Control_config[i].key_id = Control_config[i].key_default;
1204                 Control_config[i].joy_id = Control_config[i].joy_default;
1205         }
1206
1207         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1208                 Axis_map_to[i] = control_config_axis_default(i);
1209                 Invert_axis[i] = Invert_axis_defaults[i];
1210         }
1211 }
1212
1213 void control_config_scroll_screen_up()
1214 {
1215         if (Scroll_offset) {
1216                 Scroll_offset--;
1217                 Assert(Selected_line > Scroll_offset);
1218                 while (!cc_line_query_visible(Selected_line))
1219                         Selected_line--;
1220
1221                 Selected_item = -1;
1222                 gamesnd_play_iface(SND_SCROLL);
1223
1224         } else
1225                 gamesnd_play_iface(SND_GENERAL_FAIL);
1226 }
1227
1228 void control_config_scroll_line_up()
1229 {
1230         if (Selected_line) {
1231                 Selected_line--;
1232                 if (Selected_line < Scroll_offset)
1233                         Scroll_offset = Selected_line;
1234
1235                 Selected_item = -1;
1236                 gamesnd_play_iface(SND_SCROLL);
1237
1238         } else
1239                 gamesnd_play_iface(SND_GENERAL_FAIL);
1240 }
1241
1242 void control_config_scroll_screen_down()
1243 {
1244         if (Cc_lines[Num_cc_lines - 1].y + gr_get_font_height() > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
1245                 Scroll_offset++;
1246                 while (!cc_line_query_visible(Selected_line)) {
1247                         Selected_line++;
1248                         Assert(Selected_line < Num_cc_lines);
1249                 }
1250
1251                 Selected_item = -1;
1252                 gamesnd_play_iface(SND_SCROLL);
1253
1254         } else
1255                 gamesnd_play_iface(SND_GENERAL_FAIL);
1256 }
1257
1258 void control_config_scroll_line_down()
1259 {
1260         if (Selected_line < Num_cc_lines - 1) {
1261                 Selected_line++;
1262                 Assert(Selected_line > Scroll_offset);
1263                 while (!cc_line_query_visible(Selected_line))
1264                         Scroll_offset++;
1265
1266                 Selected_item = -1;
1267                 gamesnd_play_iface(SND_SCROLL);
1268
1269         } else
1270                 gamesnd_play_iface(SND_GENERAL_FAIL);
1271 }
1272
1273 void control_config_toggle_modifier(int bit)
1274 {
1275         int k, z;
1276
1277         z = Cc_lines[Selected_line].cc_index;
1278         Assert(!(z & JOY_AXIS));
1279         k = Control_config[z].key_id;
1280         if (k < 0) {
1281                 gamesnd_play_iface(SND_GENERAL_FAIL);
1282                 return;
1283         }
1284
1285         control_config_bind_key(z, k ^ bit);
1286         control_config_conflict_check();
1287         gamesnd_play_iface(SND_USER_SELECT);
1288 }
1289
1290 void control_config_toggle_invert()
1291 {
1292         int z;
1293
1294         z = Cc_lines[Selected_line].cc_index;
1295         Assert(z & JOY_AXIS);
1296         z &= ~JOY_AXIS;
1297         control_config_save_axis_undo(z);
1298         Invert_axis[z] = !Invert_axis[z];
1299 }
1300
1301 void control_config_do_bind()
1302 {
1303         int i;
1304
1305         game_flush();
1306 //      if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1307         if (Selected_line < 0) {
1308                 gamesnd_play_iface(SND_GENERAL_FAIL);
1309                 return;
1310         }
1311
1312         for (i=0; i<NUM_BUTTONS; i++)
1313                 if (i != CANCEL_BUTTON) {
1314                         CC_Buttons[gr_screen.res][i].button.reset_status();
1315                         CC_Buttons[gr_screen.res][i].button.disable();
1316                 }
1317
1318         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1319         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1320
1321         for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1322                 joy_down_count(i);  // clear checking status of all joystick buttons
1323         }
1324
1325         control_config_detect_axis_reset();
1326
1327         Binding_mode = 1;
1328         Bind_time = timer_get_milliseconds();
1329         Search_mode = 0;
1330         Last_key = -1;
1331         Axis_override = -1;
1332         gamesnd_play_iface(SND_USER_SELECT);
1333 }
1334
1335 void control_config_do_search()
1336 {
1337         int i;
1338
1339         for (i=0; i<NUM_BUTTONS; i++){
1340                 if (i != CANCEL_BUTTON) {
1341                         CC_Buttons[gr_screen.res][i].button.reset_status();
1342                         CC_Buttons[gr_screen.res][i].button.disable();
1343                 }
1344         }
1345
1346         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1347         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(KEY_ESC);
1348
1349         for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1350                 joy_down_count(i);  // clear checking status of all joystick buttons
1351         }
1352
1353         Binding_mode = 0;
1354         Search_mode = 1;
1355         Last_key = -1;
1356         gamesnd_play_iface(SND_USER_SELECT);
1357 }
1358
1359 void control_config_do_cancel(int fail = 0)
1360 {
1361         int i;
1362
1363         game_flush();
1364
1365         for (i=0; i<NUM_BUTTONS; i++){
1366                 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1367                         CC_Buttons[gr_screen.res][i].button.enable();
1368                 }
1369         }
1370
1371         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1372         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1373         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1374         CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1375         CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1376
1377         Binding_mode = Search_mode = 0;
1378         if (fail){
1379                 gamesnd_play_iface(SND_GENERAL_FAIL);
1380         } else {
1381                 gamesnd_play_iface(SND_USER_SELECT);
1382         }
1383 }
1384
1385 int control_config_accept()
1386 {
1387         int i;
1388
1389         for (i=0; i<NUM_TABS; i++)
1390                 if (Conflicts_tabs[i])
1391                         break;
1392
1393         if (i < NUM_TABS) {
1394                 gamesnd_play_iface(SND_GENERAL_FAIL);
1395                 return -1;
1396         }
1397
1398         hud_squadmsg_save_keys();  // rebuild map for saving/restoring keys in squadmsg mode
1399         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1400         gamesnd_play_iface(SND_COMMIT_PRESSED);
1401         return 0;
1402 }
1403
1404 void control_config_cancel_exit()
1405 {
1406         int i;
1407
1408         for (i=0; i<CCFG_MAX; i++)
1409                 Control_config[i] = Control_config_backup[i];
1410
1411         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1412 }
1413
1414 void control_config_button_pressed(int n)
1415 {
1416         switch (n) {
1417                 case TARGET_TAB:
1418                 case SHIP_TAB:
1419                 case WEAPON_TAB:
1420                 case COMPUTER_TAB:
1421                         Tab = n;
1422                         Scroll_offset = Selected_line = 0;
1423                         control_config_list_prepare();
1424                         gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1425                         break;
1426
1427                 case BIND_BUTTON:
1428                         control_config_do_bind();
1429                         break;
1430
1431                 case SEARCH_MODE:
1432                         control_config_do_search();
1433                         break;
1434
1435                 case SHIFT_TOGGLE:
1436                         control_config_toggle_modifier(KEY_SHIFTED);
1437                         gamesnd_play_iface(SND_USER_SELECT);
1438                         break;
1439
1440                 case ALT_TOGGLE:
1441                         control_config_toggle_modifier(KEY_ALTED);
1442                         gamesnd_play_iface(SND_USER_SELECT);
1443                         break;
1444
1445                 case INVERT_AXIS:
1446                         control_config_toggle_invert();
1447                         gamesnd_play_iface(SND_USER_SELECT);
1448                         break;
1449
1450                 case SCROLL_UP_BUTTON:
1451                         control_config_scroll_screen_up();
1452                         break;
1453
1454                 case SCROLL_DOWN_BUTTON:
1455                         control_config_scroll_screen_down();
1456                         break;
1457
1458                 case ACCEPT_BUTTON:
1459                         control_config_accept();
1460                         break;
1461
1462                 case CLEAR_BUTTON:
1463                         control_config_remove_binding();
1464                         break;
1465
1466                 case HELP_BUTTON:
1467                         launch_context_help();
1468                         gamesnd_play_iface(SND_HELP_PRESSED);
1469                         break;
1470
1471                 case RESET_BUTTON:
1472                         control_config_do_reset();
1473                         break;
1474
1475                 case UNDO_BUTTON:
1476                         control_config_undo_last();
1477                         break;
1478
1479                 case CANCEL_BUTTON:
1480                         control_config_do_cancel();
1481                         break;
1482
1483                 case CLEAR_OTHER_BUTTON:
1484                         control_config_clear_other();
1485                         break;
1486
1487                 case CLEAR_ALL_BUTTON:
1488                         control_config_clear_all();
1489                         break;          
1490         }
1491 }
1492
1493 char *control_config_tooltip_handler(char *str)
1494 {
1495         int i;
1496
1497         if (!stricmp(str, NOX("@conflict"))) {
1498                 for (i=0; i<NUM_TABS; i++) {
1499                         if (Conflicts_tabs[i])
1500                                 return XSTR( "Conflict!", 205);
1501                 }
1502         }
1503
1504         return NULL;
1505 }
1506
1507 void control_config_init()
1508 {
1509         int i;
1510         ui_button_info *b;
1511
1512         // make backup of all controls
1513         for (i=0; i<CCFG_MAX; i++)
1514                 Control_config_backup[i] = Control_config[i];
1515
1516         common_set_interface_palette(NOX("ControlConfigPalette"));  // set the interface palette
1517         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1518         Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1519         Ui_window.tooltip_handler = control_config_tooltip_handler;
1520
1521         // load in help overlay bitmap  
1522         help_overlay_load(CONTROL_CONFIG_OVERLAY);
1523         help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1524
1525         // reset conflict flashing
1526         Conflict_stamp = -1;
1527
1528         for (i=0; i<NUM_BUTTONS; i++) {
1529                 b = &CC_Buttons[gr_screen.res][i];
1530
1531                 if (b->hotspot < 0) {  // temporary
1532                         b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1);  // temporary
1533                         b->button.set_highlight_action(common_play_highlight_sound);
1534                         continue;
1535                 }
1536
1537                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1538
1539                 // set up callback for when a mouse first goes over a button
1540                 b->button.set_highlight_action(common_play_highlight_sound);            
1541                 if (i<4) {
1542                         b->button.set_bmaps(b->filename, 5, 1);         // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1543                 } else {
1544                         b->button.set_bmaps(b->filename);
1545                 }
1546                 b->button.link_hotspot(b->hotspot);
1547         }       
1548
1549         // create all text
1550         for(i=0; i<CC_NUM_TEXT; i++){
1551                 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1552         }
1553
1554         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1555                 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1556                 List_buttons[i].hide();
1557                 List_buttons[i].disable();
1558         }
1559
1560         // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1561         CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1562         CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1563         CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(KEY_ENTER);
1564         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_DELETE);
1565         CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_Z);
1566         CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(KEY_DELETE);
1567         CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
1568         CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(KEY_F1);
1569         CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_R);
1570         CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(KEY_I);
1571
1572         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1573         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1574
1575         Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);   
1576
1577         Scroll_offset = Selected_line = 0;
1578         Config_item_undo = NULL;
1579         control_config_conflict_check();
1580
1581         // setup strings                                        
1582         Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1583         Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1584         Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1585         Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1586         Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1587         Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1588         Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1589         Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1590         Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1591         Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1592         Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1593         Mouse_button_text[0] = strdup("");
1594         Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1595         Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1596         Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1597         Mouse_button_text[4] = strdup("");
1598         Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1599         Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1600         Invert_text[0] = strdup(XSTR("N", 1032));
1601         Invert_text[1] = strdup(XSTR("Y", 1033));
1602
1603         control_config_list_prepare();
1604 }
1605
1606 void control_config_close()
1607 {
1608         int idx;
1609         
1610         while (Config_item_undo){
1611                 free_undo_block();
1612         }
1613
1614         // unload the overlay bitmap
1615         help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1616         
1617         if (Background_bitmap){
1618                 bm_unload(Background_bitmap);
1619         }
1620
1621         Ui_window.destroy();
1622         common_free_interface_palette();                // restore game palette
1623         hud_squadmsg_save_keys();                               // rebuild map for saving/restoring keys in squadmsg mode
1624         game_flush();
1625         write_pilot_file();
1626
1627         // free strings 
1628         for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1629                 if(Joy_axis_action_text[idx] != NULL){
1630                         free(Joy_axis_action_text[idx]);
1631                         Joy_axis_action_text[idx] = NULL;
1632                 }
1633         }
1634         for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1635                 if(Joy_axis_text[idx] != NULL){
1636                         free(Joy_axis_text[idx]);
1637                         Joy_axis_text[idx] = NULL;
1638                 }
1639         }
1640         for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1641                 if(Mouse_button_text[idx] != NULL){
1642                         free(Mouse_button_text[idx]);
1643                         Mouse_button_text[idx] = NULL;
1644                 }
1645         }
1646         for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1647                 if(Mouse_axis_text[idx] != NULL){
1648                         free(Mouse_axis_text[idx]);
1649                         Mouse_axis_text[idx] = NULL;
1650                 }
1651         }
1652         for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1653                 if(Invert_text[idx] != NULL){
1654                         free(Invert_text[idx]);
1655                         Invert_text[idx] = NULL;
1656                 }
1657         }
1658 }
1659
1660 void control_config_do_frame(float frametime)
1661 {
1662         char buf[256], *str, *jptr;
1663         int i, j, k, w, x, y, z, len, line, conflict;
1664         int font_height = gr_get_font_height();
1665         int select_tease_line = -1;  // line mouse is down on, but won't be selected until button released
1666         static float timer = 0.0f;
1667         color *c;
1668         static int bound_timestamp = 0;
1669         static char bound_string[40];
1670         
1671         timer += frametime;
1672
1673         if (Binding_mode) {
1674                 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1675                         int bind = 0;
1676
1677                         z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1678                         i = control_config_detect_axis();
1679                         if (i >= 0) {
1680                                 Axis_override = i;
1681                                 bind = 1;
1682                         }
1683
1684                         k = game_poll();
1685                         Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1686                         Ui_window.process(0);
1687
1688                         if (k == KEY_ESC) {
1689                                 strcpy(bound_string, XSTR( "Canceled", 206));
1690                                 bound_timestamp = timestamp(2500);
1691                                 control_config_do_cancel();
1692
1693                         } else {
1694                                 if (k == KEY_ENTER)
1695                                         bind = 1;
1696
1697                                 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1698                                         if (joy_down_count(i))
1699                                                 bind = 1;
1700
1701                                 if (bind) {
1702                                         if (Axis_override >= 0) {
1703                                                 control_config_bind_axis(z, Axis_override);
1704                                                 strcpy(bound_string, Joy_axis_text[Axis_override]);
1705                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1706                                                 bound_timestamp = timestamp(2500);
1707                                                 control_config_conflict_check();
1708                                                 control_config_list_prepare();
1709                                                 control_config_do_cancel();
1710
1711                                         } else {
1712                                                 control_config_do_cancel(1);
1713                                         }
1714                                 }
1715                         }
1716
1717                 } else {
1718                         if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1719                                 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1720                                 Ui_window.set_ignore_gadgets(1);
1721                         }
1722
1723                         k = game_poll();
1724                         Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1725                         Ui_window.process(0);
1726
1727                         if ( (k > 0) || B1_JUST_RELEASED ) {
1728                                 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1729                                         help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1730                                         Ui_window.set_ignore_gadgets(0);
1731                                         k = 0;
1732                                 }
1733                         }
1734
1735                         if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1736                                 Ui_window.set_ignore_gadgets(0);
1737                         }
1738
1739                         if (k == KEY_ESC) {
1740                                 strcpy(bound_string, XSTR( "Canceled", 206));
1741                                 bound_timestamp = timestamp(2500);
1742                                 control_config_do_cancel();
1743
1744                         } else {
1745                                 switch (k & KEY_MASK) {
1746                                         case KEY_LSHIFT:
1747                                         case KEY_RSHIFT:
1748                                         case KEY_LALT:
1749                                         case KEY_RALT:
1750                                                 Last_key = k & KEY_MASK;
1751                                                 k = 0;
1752                                                 break;
1753                                 }
1754
1755                                 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED)  // a special hack just for Mike K.
1756                                         if ( (Last_key >= 0) && (k <= 0) && !keyd_pressed[Last_key] )
1757                                                 k = Last_key;
1758
1759                                 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1760                                         popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key.  Please try again.", 207));
1761                                         k = 0;
1762                                 }
1763
1764                                 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1765                                 if (k > 0) {
1766                                         z = Cc_lines[Selected_line].cc_index;
1767                                         Assert(!(z & JOY_AXIS));
1768                                         control_config_bind_key(z, k);
1769
1770                                         strcpy(bound_string, textify_scancode(k));
1771                                         gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1772                                         bound_timestamp = timestamp(2500);
1773                                         control_config_conflict_check();
1774                                         control_config_list_prepare();
1775                                         control_config_do_cancel();
1776                                 }
1777
1778                                 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1779                                         if (joy_down_count(i)) {
1780                                                 z = Cc_lines[Selected_line].cc_index;
1781                                                 Assert(!(z & JOY_AXIS));
1782                                                 control_config_bind_joy(z, i);
1783
1784                                                 strcpy(bound_string, Joy_button_text[i]);
1785                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1786                                                 bound_timestamp = timestamp(2500);
1787                                                 control_config_conflict_check();
1788                                                 control_config_list_prepare();
1789                                                 control_config_do_cancel();
1790                                                 break;
1791                                         }
1792
1793                                 if (Bind_time + 375 < timer_get_milliseconds()) {
1794                                         for (i=0; i<NUM_BUTTONS; i++){
1795                                                 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1796                                                         break;
1797                                                 }
1798                                         }
1799
1800                                         if (i == NUM_BUTTONS) {  // no buttons pressed
1801                                                 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1802                                                         if (mouse_down(1 << i)) {
1803                                                                 z = Cc_lines[Selected_line].cc_index;
1804                                                                 Assert(!(z & JOY_AXIS));
1805                                                                 control_config_bind_joy(z, i);
1806
1807                                                                 strcpy(bound_string, Joy_button_text[i]);
1808                                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1809                                                                 bound_timestamp = timestamp(2500);
1810                                                                 control_config_conflict_check();
1811                                                                 control_config_list_prepare();
1812                                                                 control_config_do_cancel();
1813                                                                 for (j=0; j<NUM_BUTTONS; j++){
1814                                                                         CC_Buttons[gr_screen.res][j].button.reset();
1815                                                                 }
1816
1817                                                                 break;
1818                                                         }
1819                                         }
1820                                 }
1821                         }
1822                 }
1823
1824         } else if (Search_mode) {
1825                 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1826                         CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1827                         Ui_window.set_ignore_gadgets(1);
1828                 }
1829
1830                 k = game_poll();
1831                 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1832                 Ui_window.process(0);
1833
1834                 if ( (k > 0) || B1_JUST_RELEASED ) {
1835                         if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1836                                 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1837                                 Ui_window.set_ignore_gadgets(0);
1838                                 k = 0;
1839                         }
1840                 }
1841
1842                 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1843                         Ui_window.set_ignore_gadgets(0);
1844                 }
1845
1846                 if (k == KEY_ESC) {
1847                         control_config_do_cancel();
1848
1849                 } else {
1850                         if ((k > 0) && !Config_allowed[k & KEY_MASK])
1851                                 k = 0;
1852
1853                         k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1854                         z = -1;
1855                         if (k > 0) {
1856                                 for (i=0; i<CCFG_MAX; i++)
1857                                         if (Control_config[i].key_id == k) {
1858                                                 z = i;
1859                                                 break;
1860                                         }
1861                         }
1862
1863                         for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1864                                 if (joy_down_count(i)) {
1865                                         j = i;
1866                                         for (i=0; i<CCFG_MAX; i++)
1867                                                 if (Control_config[i].joy_id == j) {
1868                                                         z = i;
1869                                                         break;
1870                                                 }
1871
1872                                         break;
1873                                 }
1874
1875                         // check if not on enabled button
1876                         for (j=0; j<NUM_BUTTONS; j++){
1877                                 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1878                                         break;
1879                                 }
1880                         }
1881
1882                         if (j == NUM_BUTTONS) {  // no buttons pressed
1883                                 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1884                                         if (mouse_down(1 << j)) {
1885                                                 for (i=0; i<CCFG_MAX; i++)
1886                                                         if (Control_config[i].joy_id == j) {
1887                                                                 z = i;
1888                                                                 for (j=0; j<NUM_BUTTONS; j++){
1889                                                                         CC_Buttons[gr_screen.res][j].button.reset();
1890                                                                 }
1891                                                                 break;
1892                                                         }
1893
1894                                                 break;
1895                                         }
1896                         }
1897
1898                         if (z >= 0) {
1899                                 Tab = Control_config[z].tab;
1900                                 control_config_list_prepare();
1901                                 Selected_line = Scroll_offset = 0;
1902                                 for (i=0; i<Num_cc_lines; i++)
1903                                         if (Cc_lines[i].cc_index == z) {
1904                                                 Selected_line = i;
1905                                                 break;
1906                                         }
1907
1908                                 while (!cc_line_query_visible(Selected_line)) {
1909                                         Scroll_offset++;
1910                                         Assert(Scroll_offset < Num_cc_lines);
1911                                 }
1912                         }
1913                 }
1914
1915         } else {
1916                 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
1917                 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
1918                 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
1919                 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
1920
1921                 if (!z) {
1922                         z = Cc_lines[Selected_line].cc_index;
1923                         k = Control_config[z].key_id;
1924                         if ( (k == KEY_LALT) || (k == KEY_RALT) || (k == KEY_LSHIFT) || (k == KEY_RSHIFT) ) {
1925                                 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
1926                                 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
1927                         }
1928                 }
1929
1930                 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
1931
1932                 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1933                         CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1934                         Ui_window.set_ignore_gadgets(1);
1935                 }
1936
1937                 k = Ui_window.process();
1938
1939                 if ( (k > 0) || B1_JUST_RELEASED ) {
1940                         if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1941                                 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1942                                 Ui_window.set_ignore_gadgets(0);
1943                                 k = 0;
1944                         }
1945                 }
1946
1947                 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1948                         Ui_window.set_ignore_gadgets(0);
1949                 }
1950
1951                 switch (k) {
1952                         case KEY_DOWN:  // select next line
1953                                 control_config_scroll_line_down();
1954                                 break;
1955
1956                         case KEY_UP:  // select previous line
1957                                 control_config_scroll_line_up();
1958                                 break;
1959
1960                         case KEY_SHIFTED | KEY_TAB:  // activate previous tab
1961                                 Tab--;
1962                                 if (Tab < 0)
1963                                         Tab = NUM_TABS - 1;
1964
1965                                 Scroll_offset = Selected_line = 0;
1966                                 control_config_list_prepare();
1967                                 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1968                                 break;
1969
1970                         case KEY_TAB:  // activate next tab
1971                                 Tab++;
1972                                 if (Tab >= NUM_TABS)
1973                                         Tab = 0;
1974
1975                                 Scroll_offset = Selected_line = 0;
1976                                 control_config_list_prepare();
1977                                 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1978                                 break;
1979
1980                         case KEY_LEFT:
1981                                 Selected_item--;
1982                                 if (Selected_item == -2) {
1983                                         Selected_item = 1;
1984                                         if (Cc_lines[Selected_line].jw < 1) {
1985                                                 Selected_item = 0;
1986                                                 if (Cc_lines[Selected_line].kw < 1)
1987                                                         Selected_item = -1;
1988                                         }
1989                                 }
1990
1991                                 gamesnd_play_iface(SND_SCROLL);
1992                                 break;
1993
1994                         case KEY_RIGHT:
1995                                 Selected_item++;
1996                                 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
1997                                         Selected_item = -1;
1998                                 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
1999                                         Selected_item = -1;
2000                                 else if (Selected_item > 1)
2001                                         Selected_item = -1;
2002
2003                                 gamesnd_play_iface(SND_SCROLL);
2004                                 break;
2005
2006                         case KEY_BACKSP:  // undo
2007                                 control_config_undo_last();
2008                                 break;
2009
2010                         case KEY_ESC:
2011                                 control_config_cancel_exit();
2012                                 break;
2013                 }       // end switch
2014         }
2015
2016         for (i=0; i<NUM_BUTTONS; i++){
2017                 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2018                         control_config_button_pressed(i);
2019                 }
2020         }
2021
2022         for (i=0; i<LIST_BUTTONS_MAX; i++) {
2023                 if (List_buttons[i].is_mouse_on())
2024                         select_tease_line = i + Scroll_offset;
2025         
2026                 if (List_buttons[i].pressed()) {
2027                         Selected_line = i + Scroll_offset;
2028                         Selected_item = -1;
2029                         List_buttons[i].get_mouse_pos(&x, &y);
2030                         if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2031                                 Selected_item = 0;
2032
2033                         if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2034                                 Selected_item = 1;
2035
2036                         gamesnd_play_iface(SND_USER_SELECT);
2037                 }
2038
2039                 if (List_buttons[i].double_clicked())
2040                         control_config_do_bind();
2041         }
2042
2043         GR_MAYBE_CLEAR_RES(Background_bitmap);
2044         if (Background_bitmap >= 0) {
2045                 gr_set_bitmap(Background_bitmap);
2046                 gr_bitmap(0, 0);
2047         } 
2048
2049         // highlight tab with conflict
2050         Ui_window.draw();
2051         for (i=z=0; i<NUM_TABS; i++) {
2052                 if (Conflicts_tabs[i]) {
2053                         CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2054                         z++;
2055                 }
2056         }
2057
2058         if (z) {
2059                 // maybe switch from bright to normal
2060                 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2061                         Conflict_bright = !Conflict_bright;
2062
2063                         Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2064                 }
2065
2066                 // set color and font
2067                 gr_set_font(FONT2);
2068                 if(Conflict_bright){
2069                         gr_set_color_fast(&Color_bright_red);
2070                 } else {
2071                         gr_set_color_fast(&Color_red);
2072                 }
2073
2074                 // setup the conflict string
2075                 char conflict_str[512] = "";
2076                 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2077                 int sw, sh;
2078                 gr_get_string_size(&sw, &sh, conflict_str);
2079
2080                 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2081
2082                 gr_set_font(FONT1);
2083         } else {
2084                 // might as well always reset the conflict stamp
2085                 Conflict_stamp = -1;
2086         }
2087
2088         for (i=0; i<NUM_TABS; i++) {
2089                 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2090                         break;
2091                 }
2092         }
2093
2094         if (i == NUM_TABS) {
2095                 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2096         }
2097
2098         if (Search_mode) {
2099                 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2100         }
2101
2102         if (Selected_line >= 0) {
2103                 z = Cc_lines[Selected_line].cc_index;
2104                 if (z & JOY_AXIS) {
2105                         if (Invert_axis[z & ~JOY_AXIS]) {
2106                                 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2107                         }
2108
2109                 } else {
2110                         z = Control_config[z].key_id;
2111                         if (z >= 0) {
2112                                 if (z & KEY_SHIFTED) {
2113                                         CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2114                                 }
2115                                 if (z & KEY_ALTED) {
2116                                         CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2117                                 }
2118                         }
2119                 }
2120         }
2121
2122         if (Binding_mode) {
2123                 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2124         }
2125
2126         z = Cc_lines[Selected_line].cc_index;
2127         x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2128         y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2129         if (Binding_mode) {
2130                 int t;
2131
2132                 t = (int) (timer * 3);
2133                 if (t % 2) {
2134                         gr_set_color_fast(&Color_text_normal);
2135                         gr_get_string_size(&w, NULL, XSTR( "?", 208));
2136                         gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2137                 }
2138
2139         } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2140                 i = Conflicts[z].key;
2141                 if (i < 0)
2142                         i = Conflicts[z].joy;
2143
2144                 gr_set_color_fast(&Color_text_normal);
2145                 str = XSTR( "Control conflicts with:", 209);
2146                 gr_get_string_size(&w, NULL, str);
2147                 gr_printf(x - w / 2, y - font_height, str);
2148
2149                 strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
2150                 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2151                 gr_get_string_size(&w, NULL, buf);
2152                 gr_printf(x - w / 2, y, buf);
2153
2154         } else if (*bound_string) {
2155                 gr_set_color_fast(&Color_text_normal);
2156                 gr_get_string_size(&w, NULL, bound_string);
2157                 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2158                 if (timestamp_elapsed(bound_timestamp))
2159                         *bound_string = 0;
2160         }
2161
2162 //      gr_set_color_fast(&Color_text_heading);
2163 //      gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2164
2165 //      gr_get_string_size(&w, &h, Heading[Tab]);
2166 //      y = HEADING_Y + h / 2 - 1;
2167 //      gr_line(LIST_X, y, LIST_X + 18, y);
2168 //      gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2169
2170         if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2171                 gr_set_color_fast(&Color_white);
2172                 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2173         }
2174
2175         conflict = 0;
2176         line = Scroll_offset;
2177         while (cc_line_query_visible(line)) {
2178                 z = Cc_lines[line].cc_index;
2179                 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2180
2181                 List_buttons[line - Scroll_offset].update_dimensions(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, Control_list_coords[gr_screen.res][CONTROL_W_COORD], font_height);
2182                 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2183
2184                 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2185
2186                 if (line == Selected_line){
2187                         c = &Color_text_selected;
2188                 } else if (line == select_tease_line) {
2189                         c = &Color_text_subselected;
2190                 } else {
2191                         c = &Color_text_normal;
2192                 }
2193
2194                 gr_set_color_fast(c);
2195                 if (Cc_lines[line].label) {
2196                         strcpy(buf, Cc_lines[line].label);
2197                         gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2198                         gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2199                 }
2200
2201                 if (!(z & JOY_AXIS)) {
2202                         k = Control_config[z].key_id;
2203                         j = Control_config[z].joy_id;
2204                         x = Control_list_key_x[gr_screen.res];
2205                         jptr = NULL;
2206                         *buf = 0;
2207
2208                         if ((k < 0) && (j < 0)) {
2209                                 gr_set_color_fast(&Color_grey);
2210                                 gr_printf(x, y, XSTR( "None", 211));
2211
2212                         } else {
2213                                 if (k >= 0) {
2214                                         strcpy(buf, textify_scancode(k));
2215                                         if (Conflicts[z].key >= 0) {
2216                                                 if (c == &Color_text_normal)
2217                                                         gr_set_color_fast(&Color_text_error);
2218                                                 else {
2219                                                         gr_set_color_fast(&Color_text_error_hi);
2220                                                         conflict++;
2221                                                 }
2222
2223                                         } else if (Selected_item == 1) {
2224                                                 gr_set_color_fast(&Color_text_normal);
2225
2226                                         } else
2227                                                 gr_set_color_fast(c);
2228
2229                                         gr_printf(x, y, buf);
2230
2231                                         len = strlen(buf);
2232                                         Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2233                                         gr_get_string_size(&w, NULL, buf);
2234                                         Cc_lines[line].kw = w;
2235                                         x += w;
2236
2237                                         if (j >= 0) {
2238                                                 gr_set_color_fast(&Color_text_normal);
2239                                                 gr_printf(x, y, XSTR( ", ", 212));
2240                                                 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2241                                                 x += w;
2242                                         }
2243                                 }
2244
2245                                 if (j >= 0) {
2246                                         strcpy(buf, Joy_button_text[j]);
2247                                         if (Conflicts[z].joy >= 0) {
2248                                                 if (c == &Color_text_normal)
2249                                                         gr_set_color_fast(&Color_text_error);
2250                                                 else {
2251                                                         gr_set_color_fast(&Color_text_error_hi);
2252                                                         conflict++;
2253                                                 }
2254
2255                                         } else if (!Selected_item) {
2256                                                 gr_set_color_fast(&Color_text_normal);
2257
2258                                         } else
2259                                                 gr_set_color_fast(c);
2260
2261                                         gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
2262                                         gr_printf(x, y, buf);
2263
2264                                         Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2265                                         gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2266                                 }
2267                         }
2268
2269                 } else {
2270                         x = Control_list_key_x[gr_screen.res];
2271                         j = Axis_map_to[z & ~JOY_AXIS];
2272                         if (Binding_mode && (line == Selected_line))
2273                                 j = Axis_override;
2274
2275                         if (j < 0) {
2276                                 gr_set_color_fast(&Color_grey);
2277                                 gr_printf(x, y, XSTR( "None", 211));
2278
2279                         } else {
2280                                 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2281                                         if (c == &Color_text_normal)
2282                                                 gr_set_color_fast(&Color_text_error);
2283
2284                                         else {
2285                                                 gr_set_color_fast(&Color_text_error_hi);
2286                                                 conflict++;
2287                                         }
2288
2289                                 } else if (!Selected_item) {
2290                                         gr_set_color_fast(&Color_text_normal);
2291
2292                                 } else
2293                                         gr_set_color_fast(c);
2294
2295                                 gr_string(x, y, Joy_axis_text[j]);
2296                         }
2297                 }
2298
2299                 line++;
2300         }
2301
2302         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2303
2304         i = line - Scroll_offset;
2305         while (i < LIST_BUTTONS_MAX)
2306                 List_buttons[i++].disable();
2307
2308         // blit help overlay if active
2309         help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2310
2311         gr_flip();
2312 }
2313
2314 void clear_key_binding(short key)
2315 {
2316         int i;
2317
2318         for (i=0; i<CCFG_MAX; i++) {
2319                 if (Control_config[i].key_id == key)
2320                         Control_config[i].key_id = -1;
2321         }
2322 }
2323
2324 float check_control_timef(int id)
2325 {
2326         float t1, t2;
2327
2328         // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2329         Assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
2330
2331         // first, see if control actually used (makes sure modifiers match as well)
2332         if (!check_control(id))
2333                 return 0.0f;
2334
2335         t1 = key_down_timef(Control_config[id].key_id);
2336         if (t1)
2337                 control_used(id);
2338
2339         t2 = joy_down_time(Control_config[id].joy_id);
2340         if (t2)
2341                 control_used(id);
2342
2343         if (t1 + t2)
2344                 return t1 + t2;
2345
2346         return 1.0f;
2347 }
2348
2349 void control_check_indicate()
2350 {
2351 #ifndef NDEBUG
2352         if (Show_controls_info) {
2353                 gr_set_color_fast(&HUD_color_debug);
2354                 gr_printf(490, 15, NOX("Ctrls checked: %d"), Control_check_count);
2355         }
2356 #endif
2357
2358         Control_check_count = 0;
2359 }
2360
2361 int check_control(int id, int key)
2362 {
2363         int z, mask;
2364         static int last_key = 0;
2365
2366         Control_check_count++;
2367         if (key < 0)
2368                 key = last_key;
2369
2370         last_key = key;
2371
2372         // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2373         if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
2374                 return 0;
2375         }
2376
2377         if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2378                 if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id)) {
2379                         control_used(id);
2380                         return 1;
2381                 }
2382
2383                 if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS))
2384                         if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2385                                 control_used(id);
2386                                 return 1;
2387                         }
2388
2389                 // check what current modifiers are pressed
2390                 mask = 0;
2391                 if (keyd_pressed[KEY_LSHIFT] || key_down_count(KEY_LSHIFT) || keyd_pressed[KEY_RSHIFT] || key_down_count(KEY_RSHIFT))
2392                         mask |= KEY_SHIFTED;
2393
2394                 if (keyd_pressed[KEY_LALT] || key_down_count(KEY_LALT) || keyd_pressed[KEY_RALT] || key_down_count(KEY_RALT))
2395                         mask |= KEY_ALTED;
2396
2397                 z = Control_config[id].key_id;
2398                 if (z >= 0) {
2399                         if ( (z != KEY_LALT) && (z != KEY_RALT) && (z != KEY_LSHIFT) && (z != KEY_RSHIFT) ) {
2400                                 // if current modifiers don't match action's modifiers, don't register control active.
2401                                 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2402                                         return 0;
2403                         }
2404
2405                         z &= KEY_MASK;
2406
2407                         if (keyd_pressed[z] || key_down_count(z)) {
2408                                 if ( !hud_squadmsg_read_key(z) ) {
2409                                         control_used(id);
2410                                         return 1;
2411                                 }
2412                         }
2413                 }
2414
2415                 return 0;
2416         }
2417
2418         if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2419                 control_used(id);
2420                 return 1;
2421         }
2422
2423         return 0;
2424 }
2425
2426 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2427 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2428 {
2429         int axes_values[JOY_NUM_AXES];
2430
2431         joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
2432
2433         //      joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2434         *h = 0;
2435         if (Axis_map_to[0] >= 0)
2436                 *h = joy_get_scaled_reading(axes_values[Axis_map_to[0]], Axis_map_to[0]);
2437
2438         *p = 0;
2439         if (Axis_map_to[1] >= 0)
2440                 *p = joy_get_scaled_reading(axes_values[Axis_map_to[1]], Axis_map_to[1]);
2441
2442         *b = 0;
2443         if (Axis_map_to[2] >= 0)
2444                 *b = joy_get_scaled_reading(axes_values[Axis_map_to[2]], Axis_map_to[2]);
2445
2446         *ta = 0;
2447         if (Axis_map_to[3] >= 0)
2448                 *ta = joy_get_unscaled_reading(axes_values[Axis_map_to[3]], Axis_map_to[3]);
2449
2450         *tr = 0;
2451         if (Axis_map_to[4] >= 0)
2452                 *tr = joy_get_scaled_reading(axes_values[Axis_map_to[4]], Axis_map_to[4]);
2453
2454         if (Invert_axis[0])
2455                 *h = -(*h);
2456         if (Invert_axis[1])
2457                 *p = -(*p);
2458         if (Invert_axis[2])
2459                 *b = -(*b);
2460         if (Invert_axis[3])
2461                 *ta = F1_0 - *ta;
2462         if (Invert_axis[4])
2463                 *tr = -(*tr);
2464
2465         return;
2466 }
2467
2468 void control_used(int id)
2469 {
2470         Control_config[id].used = timestamp();
2471 }
2472
2473 void control_config_clear_used_status()
2474 {
2475         int i;
2476
2477         for (i=0; i<CCFG_MAX; i++)
2478                 Control_config[i].used = 0;
2479 }
2480
2481 void control_config_clear()
2482 {
2483         int i;
2484
2485         // Reset keyboard defaults
2486         for (i=0; i<CCFG_MAX; i++)
2487                 Control_config[i].key_id = Control_config[i].joy_id = -1;
2488 }
2489
2490 int control_config_handle_conflict()
2491 {/*
2492         if ((Selected_item == -1) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {  // we are deleting a conflict
2493                 j = Conflicts[z].joy;
2494                 if ((j >= 0) && (Control_config[j].joy_id < 0))
2495                         j = -1;
2496
2497                 k = Conflicts[z].key;
2498                 if ((k >= 0) && (Control_config[k].key_id < 0))
2499                         k = -1;
2500
2501                 if ((j >= 0) && (k >= 0) && (j != k)) {  // deleting 2 conflicts, each in different actions
2502                         ptr = get_undo_block(2);
2503                         ptr->index[0] = j;
2504                         ptr->list[0] = Control_config[j];
2505                         Control_config[j].joy_id = (short) -1;
2506
2507                         ptr->index[1] = k;
2508                         ptr->list[1] = Control_config[k];
2509                         Control_config[k].key_id = (short) -1;
2510
2511                 } else {  // only 1 action in conflict with selected action (might be both controls, though)
2512                         z = j;
2513                         if (j < 0)
2514                                 z = k;
2515
2516                         Assert(z >= 0);
2517                         ptr = get_undo_block(1);
2518                         ptr->index[0] = z;
2519                         ptr->list[0] = Control_config[z];
2520
2521                         if (j >= 0)
2522                                 Control_config[z].joy_id = (short) -1;
2523
2524                         if (k >= 0)
2525                                 Control_config[z].key_id = (short) -1;
2526                 }*/
2527
2528         return 0;
2529 }