summaryrefslogblamecommitdiffstatshomepage
path: root/src/osd/windows/input.c
blob: 36aad03a0877b4edd04f7063453e591c55cbede3 (plain) (tree)
1
2
3
4
5
6
7
8
9



                                                              




                                                              

                                                              

                                 

                                                            
                             


                                                            
                                                       


                                                            

                                 











                                                              
                                                 



                                                              
                             
 







                                                           
                   
                






                     
                
                     










                     











                                                              
                               
 



                             






                                                              
                                                                                      

              
                               
     
                               








                                                                            
                     
 


                                                  



                                                                         
                  
 

                                                    



                                                                            
                     
 


                                            



                                                  
                         
 



                                        



                                               
                           
 
                                       



                             
                 
 




                                                              
 
                             



                                                           

                           
                                       



                       


                                             


                                              

                                         

        
                                          



                



                                                                                                                                                                   







                                                              



                                          

                        

                                           

                     



                                                                  

                  

                                                             

               

                                          

                  


                                                      

                  
                                          

                     
                                               










                                                              


                                                      





                                                                 
                                                                                                                      






                                                                                  
                                                 
                                                 



                                                      
                                                  
                                                  
                                                                                                                                  
                                                                                                                                                                                                            
                                                        
                                                                                                                    









                                                                                       
                                                    
                                                    
                                                                                                                                 

                                                        
                                                                                         
                                                                       
                                                                                      

















                                                                         












































































































                                                                           








                                                                              











                                                                                


  



                                                              
                                                       


                                                           
                                       









                                                                                    
                                                                                      


































                                                                                                                
                                            

                                           


                                                                                                              





                                                                                                        
                                                                                                   







                                                                                   
                               






                                                              
                                                    

                                         






                                                     






                                                              
                                                   














                                                                              
                                            
 
                                                              

                                



                                              
 

                                                                                  
                                                           
 
                                                                                

                                                                                                 
         













































                                                                     
                                                                     




                                
                             


                                                      
                             































                                                                                                                   
                    
























                                                                                                         
                                                      






























                                                                                                         
                                  




                                                              





                                                              
                                                                                                   
         
                                                  
                             
 









                                                                                            
                             

                                                              
                                                                                              
 
                                
 
                                 
                                                                            
                                      



                                                                          
                                                                                                                                                        



                                                   

                                                                                                  
                                      


                                                      
                                                                                              
                                                                                                                                       



                                                       


                                                                                                
 


                                                                                               
                                                                                                                 

                                      

                                                      
                                      


































                                                                          
                                                                                                                     




                                                
                                                           













                                                                                                  
                             


















                                                                                                                            
                                                


                                
                             




















































                                                                                                  

                                                              

                                   
                                              









                                                                               

                                                              

                                   
                                              







                                                              
                                                







                                                                                              
                                                                                                       



                                              
                                                                                                                 



                                     
                                                                                           



                                    
                                                                                                                         



                                                         
                                                                                   
                                                                                                                                                              
                                       




                                                      
                                                                                    
                                                                                                                                                                       
                                       








                                                              
                                                

















































































                                                                                                
                                                 

                       




                                                      
                                             
                                                                                                                      








                                                    




















                                                                                                         

      


                                                                                        
                                                                                                        




                                                                         
                                                                                                                                   







                                                                                                                 
                                                                                                                             




                                                                                                            
                                                                                                                           








                                                                                                         
                                                 



                                                                             

                                                   

































                                                                                                                                 
                                                                                                                                                                                                           




                                                
                                                                                             

                                     
                                                                                                                       



                                               
                                                                                                                                                    




























































                                                                                                                          
                                                                                                                   























                                                                                                            
                                                                                            





                                                                 
                              


































                                                                                                                                                         
                                                           



                                            
                                                                                                                                        



                            
                                                                                                                 






                                                                               
                           





                                                                                     
                                                                                                                    
                               




























                                                                                              
                                                           



                                            
                                                                                                                                           






                                                                                          
                                                                                                   





                                                                                                                        
                                                         

                                                                                                                                                            



                           
                                                                                                              


                                          
                                                                                                                         









                                                                                                                               
                                                                                                                                                     

                                                                                   
                                                                                                                                                      
                                                   
                                                                                                                                                              
 
                               




                                                                           
                                                                                  
                                                                                                         

                                                                                   
                                                                                           
                                                                                                                                                               
                                    
                                                                                                                                                                       
 
                               



































                                                                                   
                                                                        
                                               
                                                           


                             
                                                                       
                                                                          
         
                                            
                                                                                                                    




                                                                                                                        
                                                         
                                                                                                                                                                    


                                                                                                       
                                                         
                                                                                                                                                                 


                                                                                                             
                                                         
                                                                                                                                                                   






                                                                                  
                                                                                                                 





                                                                                                        
                           






                                                                                                          

                                    




                                                                                                                                              
                                                                                                                                                         
                               






                                                                        
                           


                                                                                                                                                      
                                                                                                                                               
                               


                                                                                                                                                      
                                                                                                                                                
                               


                                                                                                                                                      
                                                                                                                                             
                               


                                                                                                                                                      
                                                                                                                                               
                               




                                                                           
                                                                                 
                                                                                                         
                                                                                                                                                                                                         
                               
































                                                                                                                                 
                                                              





                                             
                                              








                                                      



                                                                                      








                                                              
                                                   






                                           
                                                                                                          



















                                                                                                                                                   
                                                                       









                                                                                        
                                                                


                                                                               
                                                             

         
                                                                   






















                                                                                         
                             



                            
                                     






                                                              
                                                   














                                                                               
                                                                                                                                







                                                                                                   
                                                       








                                                                                                     
                                                                         
                           






                                                   
                                   

















                                                              
                                



                                                              

                                                                                        








                                

                                                                   

                                     
 
                                                     
                                                                                              


                                                                                  
                                                                                   






















































































                                                                                                                              
                                                                         














                                                            
                          






                                                       
                                                             



                              
                                       
                            
                                     










                                                              
                                                                                        




                                            
                                                                          



                            
                                                                                                                 





                                                                               
                           






                                                                                                                           
                                                                                                                    
                               




































                                                                                                             
                                                                                     




                                                  
                                                                       






                                                                                          
                                                                                  



                                        
                                                                                                              

                            
                                                                                                                         
                                     




                                                 
                                                                           

                                                                                   
                                                                                                                                                      
                                                   
                                                                                                                                                              
 
                               




                                              
                                                                            

                                                                                   
                                                                                                                                                               
                                    
                                                                                                                                                                       
 
                               
















                                                                                                  













                                                                                                                     

                                                                                                           


                                                                                                  









                                                                                                                    










                                                              
                                              




























                                                                        
                                                                      







                                                                                  
                            

























                                                                       
//============================================================
//
//  input.c - Win32 implementation of MAME input routines
//
//============================================================
//
//  Copyright Aaron Giles
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or
//  without modification, are permitted provided that the
//  following conditions are met:
//
//    * Redistributions of source code must retain the above
//      copyright notice, this list of conditions and the
//      following disclaimer.
//    * Redistributions in binary form must reproduce the
//      above copyright notice, this list of conditions and
//      the following disclaimer in the documentation and/or
//      other materials provided with the distribution.
//    * Neither the name 'MAME' nor the names of its
//      contributors may be used to endorse or promote
//      products derived from this software without specific
//      prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND
//  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
//  EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
//  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//  DAMAGE (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
//  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
//  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
//  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//============================================================

// For testing purposes: force DirectInput
#define FORCE_DIRECTINPUT   0

// standard windows headers
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winioctl.h>
#include <tchar.h>

// undef WINNT for dinput.h to prevent duplicate definition
#undef WINNT
#include <dinput.h>
#undef interface

// standard C headers
#include <conio.h>
#include <ctype.h>
#include <stddef.h>

// MAME headers
#include "emu.h"
#include "osdepend.h"
#include "ui.h"

// MAMEOS headers
#include "winmain.h"
#include "window.h"
#include "input.h"
#include "debugwin.h"
#include "video.h"
#include "strconv.h"
#include "config.h"

//============================================================
//  PARAMETERS
//============================================================

enum
{
	POVDIR_LEFT = 0,
	POVDIR_RIGHT,
	POVDIR_UP,
	POVDIR_DOWN
};

#define MAX_KEYS            256

#define MAME_KEY            0
#define DI_KEY              1
#define VIRTUAL_KEY         2
#define ASCII_KEY           3



//============================================================
//  MACROS
//============================================================

#define STRUCTSIZE(x)       ((dinput_version == 0x0300) ? sizeof(x##_DX3) : sizeof(x))

#ifdef UNICODE
#define UNICODE_SUFFIX      "W"
#else
#define UNICODE_SUFFIX      "A"
#endif



//============================================================
//  TYPEDEFS
//============================================================

// state information for a keyboard; DirectInput state must be first element
struct keyboard_state
{
	UINT8                   state[MAX_KEYS];
	INT8                    oldkey[MAX_KEYS];
	INT8                    currkey[MAX_KEYS];
};


// state information for a mouse; DirectInput state must be first element
struct mouse_state
{
	DIMOUSESTATE2           state;
	LONG                    raw_x, raw_y, raw_z;
};


// state information for a joystick; DirectInput state must be first element
struct joystick_state
{
	DIJOYSTATE              state;
	LONG                    rangemin[8];
	LONG                    rangemax[8];
};


// DirectInput-specific information about a device
struct dinput_device_info
{
	LPDIRECTINPUTDEVICE     device;
	LPDIRECTINPUTDEVICE2    device2;
	DIDEVCAPS               caps;
	LPCDIDATAFORMAT         format;
};


// RawInput-specific information about a device
struct rawinput_device_info
{
	HANDLE                  device;
};


// generic device information
class device_info
{
public:
	device_info(running_machine &machine)
		: m_machine(machine) { }

	running_machine &machine() const { return m_machine; }

	// device information
	device_info **          head;
	device_info *           next;
	const char *            name;
	void                    (*poll)(device_info *info);

	// MAME information
	input_device *          device;

	// device state
	union
	{
		keyboard_state      keyboard;
		mouse_state         mouse;
		joystick_state      joystick;
	};

	// DirectInput/RawInput-specific state
	dinput_device_info      dinput;
	rawinput_device_info    rawinput;

private:
	running_machine &       m_machine;
};


// RawInput APIs
typedef /*WINUSERAPI*/ INT (WINAPI *get_rawinput_device_list_ptr)(OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PINT puiNumDevices, IN UINT cbSize);
typedef /*WINUSERAPI*/ INT (WINAPI *get_rawinput_data_ptr)(IN HRAWINPUT hRawInput, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize, IN UINT cbSizeHeader);
typedef /*WINUSERAPI*/ INT (WINAPI *get_rawinput_device_info_ptr)(IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize);
typedef /*WINUSERAPI*/ BOOL (WINAPI *register_rawinput_devices_ptr)(IN PCRAWINPUTDEVICE pRawInputDevices, IN UINT uiNumDevices, IN UINT cbSize);



//============================================================
//  LOCAL VARIABLES
//============================================================

// global states
static UINT8                input_enabled;
static osd_lock *           input_lock;
static UINT8                input_paused;
static DWORD                last_poll;

// DirectInput variables
static LPDIRECTINPUT        dinput;
static int                  dinput_version;

// RawInput variables
static get_rawinput_device_list_ptr     get_rawinput_device_list;
static get_rawinput_data_ptr            get_rawinput_data;
static get_rawinput_device_info_ptr     get_rawinput_device_info;
static register_rawinput_devices_ptr    register_rawinput_devices;

// keyboard states
static UINT8                keyboard_win32_reported_key_down;
static device_info *        keyboard_list;

// mouse states
static UINT8                mouse_enabled;
static device_info *        mouse_list;

// lightgun states
static UINT8                lightgun_shared_axis_mode;
static UINT8                lightgun_enabled;
static device_info *        lightgun_list;

// joystick states
static device_info *        joystick_list;

// default axis names
static const TCHAR *const default_axis_name[] =
{
	TEXT("X"), TEXT("Y"), TEXT("Z"), TEXT("RX"),
	TEXT("RY"), TEXT("RZ"), TEXT("SL1"), TEXT("SL2")
};



//============================================================
//  PROTOTYPES
//============================================================

static void wininput_pause(running_machine &machine);
static void wininput_resume(running_machine &machine);
static void wininput_exit(running_machine &machine);

// device list management
static void device_list_poll_devices(device_info *devlist_head);
static void device_list_reset_devices(device_info *devlist_head);

// generic device management
static device_info *generic_device_alloc(running_machine &machine, device_info **devlist_head_ptr, const TCHAR *name);
static void generic_device_free(device_info *devinfo);
static int generic_device_index(device_info *devlist_head, device_info *devinfo);
static void generic_device_reset(device_info *devinfo);
static INT32 generic_button_get_state(void *device_internal, void *item_internal);
static INT32 generic_axis_get_state(void *device_internal, void *item_internal);

// Win32-specific input code
static void win32_init(running_machine &machine);
static void win32_exit(running_machine &machine);
static void win32_keyboard_poll(device_info *devinfo);
static void win32_lightgun_poll(device_info *devinfo);

// DirectInput-specific code
static void dinput_init(running_machine &machine);
static void dinput_exit(running_machine &machine);
static HRESULT dinput_set_dword_property(LPDIRECTINPUTDEVICE device, REFGUID property_guid, DWORD object, DWORD how, DWORD value);
static device_info *dinput_device_create(running_machine &machine, device_info **devlist_head_ptr, LPCDIDEVICEINSTANCE instance, LPCDIDATAFORMAT format1, LPCDIDATAFORMAT format2, DWORD cooperative_level);
static void dinput_device_release(device_info *devinfo);
static char *dinput_device_item_name(device_info *devinfo, int offset, const TCHAR *defstring, const TCHAR *suffix);
static HRESULT dinput_device_poll(device_info *devinfo);
static BOOL CALLBACK dinput_keyboard_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref);
static void dinput_keyboard_poll(device_info *devinfo);
static BOOL CALLBACK dinput_mouse_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref);
static void dinput_mouse_poll(device_info *devinfo);
static BOOL CALLBACK dinput_joystick_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref);
static void dinput_joystick_poll(device_info *devinfo);
static INT32 dinput_joystick_pov_get_state(void *device_internal, void *item_internal);

// RawInput-specific code
static void rawinput_init(running_machine &machine);
static void rawinput_exit(running_machine &machine);
static device_info *rawinput_device_create(running_machine &machine, device_info **devlist_head_ptr, PRAWINPUTDEVICELIST device);
static void rawinput_device_release(device_info *info);
static TCHAR *rawinput_device_improve_name(TCHAR *name);
static void rawinput_keyboard_enum(running_machine &machine, PRAWINPUTDEVICELIST device);
static void rawinput_keyboard_update(HANDLE device, RAWKEYBOARD *data);
static void rawinput_mouse_enum(running_machine &machine, PRAWINPUTDEVICELIST device);
static void rawinput_mouse_update(HANDLE device, RAWMOUSE *data);
static void rawinput_mouse_poll(device_info *devinfo);

// misc utilities
static TCHAR *reg_query_string(HKEY key, const TCHAR *path);
static const TCHAR *default_button_name(int which);
static const TCHAR *default_pov_name(int which);



//============================================================
//  KEYBOARD/JOYSTICK LIST
//============================================================

// master keyboard translation table
static const int win_key_trans_table[][4] =
{
	// MAME key             dinput key          virtual key     ascii
	{ ITEM_ID_ESC,          DIK_ESCAPE,         VK_ESCAPE,      27 },
	{ ITEM_ID_1,            DIK_1,              '1',            '1' },
	{ ITEM_ID_2,            DIK_2,              '2',            '2' },
	{ ITEM_ID_3,            DIK_3,              '3',            '3' },
	{ ITEM_ID_4,            DIK_4,              '4',            '4' },
	{ ITEM_ID_5,            DIK_5,              '5',            '5' },
	{ ITEM_ID_6,            DIK_6,              '6',            '6' },
	{ ITEM_ID_7,            DIK_7,              '7',            '7' },
	{ ITEM_ID_8,            DIK_8,              '8',            '8' },
	{ ITEM_ID_9,            DIK_9,              '9',            '9' },
	{ ITEM_ID_0,            DIK_0,              '0',            '0' },
	{ ITEM_ID_MINUS,        DIK_MINUS,          VK_OEM_MINUS,   '-' },
	{ ITEM_ID_EQUALS,       DIK_EQUALS,         VK_OEM_PLUS,    '=' },
	{ ITEM_ID_BACKSPACE,    DIK_BACK,           VK_BACK,        8 },
	{ ITEM_ID_TAB,          DIK_TAB,            VK_TAB,         9 },
	{ ITEM_ID_Q,            DIK_Q,              'Q',            'Q' },
	{ ITEM_ID_W,            DIK_W,              'W',            'W' },
	{ ITEM_ID_E,            DIK_E,              'E',            'E' },
	{ ITEM_ID_R,            DIK_R,              'R',            'R' },
	{ ITEM_ID_T,            DIK_T,              'T',            'T' },
	{ ITEM_ID_Y,            DIK_Y,              'Y',            'Y' },
	{ ITEM_ID_U,            DIK_U,              'U',            'U' },
	{ ITEM_ID_I,            DIK_I,              'I',            'I' },
	{ ITEM_ID_O,            DIK_O,              'O',            'O' },
	{ ITEM_ID_P,            DIK_P,              'P',            'P' },
	{ ITEM_ID_OPENBRACE,    DIK_LBRACKET,       VK_OEM_4,       '[' },
	{ ITEM_ID_CLOSEBRACE,   DIK_RBRACKET,       VK_OEM_6,       ']' },
	{ ITEM_ID_ENTER,        DIK_RETURN,         VK_RETURN,      13 },
	{ ITEM_ID_LCONTROL,     DIK_LCONTROL,       VK_LCONTROL,    0 },
	{ ITEM_ID_A,            DIK_A,              'A',            'A' },
	{ ITEM_ID_S,            DIK_S,              'S',            'S' },
	{ ITEM_ID_D,            DIK_D,              'D',            'D' },
	{ ITEM_ID_F,            DIK_F,              'F',            'F' },
	{ ITEM_ID_G,            DIK_G,              'G',            'G' },
	{ ITEM_ID_H,            DIK_H,              'H',            'H' },
	{ ITEM_ID_J,            DIK_J,              'J',            'J' },
	{ ITEM_ID_K,            DIK_K,              'K',            'K' },
	{ ITEM_ID_L,            DIK_L,              'L',            'L' },
	{ ITEM_ID_COLON,        DIK_SEMICOLON,      VK_OEM_1,       ';' },
	{ ITEM_ID_QUOTE,        DIK_APOSTROPHE,     VK_OEM_7,       '\'' },
	{ ITEM_ID_TILDE,        DIK_GRAVE,          VK_OEM_3,       '`' },
	{ ITEM_ID_LSHIFT,       DIK_LSHIFT,         VK_LSHIFT,      0 },
	{ ITEM_ID_BACKSLASH,    DIK_BACKSLASH,      VK_OEM_5,       '\\' },
	{ ITEM_ID_BACKSLASH2,   DIK_OEM_102,        VK_OEM_102,     '<' },
	{ ITEM_ID_Z,            DIK_Z,              'Z',            'Z' },
	{ ITEM_ID_X,            DIK_X,              'X',            'X' },
	{ ITEM_ID_C,            DIK_C,              'C',            'C' },
	{ ITEM_ID_V,            DIK_V,              'V',            'V' },
	{ ITEM_ID_B,            DIK_B,              'B',            'B' },
	{ ITEM_ID_N,            DIK_N,              'N',            'N' },
	{ ITEM_ID_M,            DIK_M,              'M',            'M' },
	{ ITEM_ID_COMMA,        DIK_COMMA,          VK_OEM_COMMA,   ',' },
	{ ITEM_ID_STOP,         DIK_PERIOD,         VK_OEM_PERIOD,  '.' },
	{ ITEM_ID_SLASH,        DIK_SLASH,          VK_OEM_2,       '/' },
	{ ITEM_ID_RSHIFT,       DIK_RSHIFT,         VK_RSHIFT,      0 },
	{ ITEM_ID_ASTERISK,     DIK_MULTIPLY,       VK_MULTIPLY,    '*' },
	{ ITEM_ID_LALT,         DIK_LMENU,          VK_LMENU,       0 },
	{ ITEM_ID_SPACE,        DIK_SPACE,          VK_SPACE,       ' ' },
	{ ITEM_ID_CAPSLOCK,     DIK_CAPITAL,        VK_CAPITAL,     0 },
	{ ITEM_ID_F1,           DIK_F1,             VK_F1,          0 },
	{ ITEM_ID_F2,           DIK_F2,             VK_F2,          0 },
	{ ITEM_ID_F3,           DIK_F3,             VK_F3,          0 },
	{ ITEM_ID_F4,           DIK_F4,             VK_F4,          0 },
	{ ITEM_ID_F5,           DIK_F5,             VK_F5,          0 },
	{ ITEM_ID_F6,           DIK_F6,             VK_F6,          0 },
	{ ITEM_ID_F7,           DIK_F7,             VK_F7,          0 },
	{ ITEM_ID_F8,           DIK_F8,             VK_F8,          0 },
	{ ITEM_ID_F9,           DIK_F9,             VK_F9,          0 },
	{ ITEM_ID_F10,          DIK_F10,            VK_F10,         0 },
	{ ITEM_ID_NUMLOCK,      DIK_NUMLOCK,        VK_NUMLOCK,     0 },
	{ ITEM_ID_SCRLOCK,      DIK_SCROLL,         VK_SCROLL,      0 },
	{ ITEM_ID_7_PAD,        DIK_NUMPAD7,        VK_NUMPAD7,     0 },
	{ ITEM_ID_8_PAD,        DIK_NUMPAD8,        VK_NUMPAD8,     0 },
	{ ITEM_ID_9_PAD,        DIK_NUMPAD9,        VK_NUMPAD9,     0 },
	{ ITEM_ID_MINUS_PAD,    DIK_SUBTRACT,       VK_SUBTRACT,    0 },
	{ ITEM_ID_4_PAD,        DIK_NUMPAD4,        VK_NUMPAD4,     0 },
	{ ITEM_ID_5_PAD,        DIK_NUMPAD5,        VK_NUMPAD5,     0 },
	{ ITEM_ID_6_PAD,        DIK_NUMPAD6,        VK_NUMPAD6,     0 },
	{ ITEM_ID_PLUS_PAD,     DIK_ADD,            VK_ADD,         0 },
	{ ITEM_ID_1_PAD,        DIK_NUMPAD1,        VK_NUMPAD1,     0 },
	{ ITEM_ID_2_PAD,        DIK_NUMPAD2,        VK_NUMPAD2,     0 },
	{ ITEM_ID_3_PAD,        DIK_NUMPAD3,        VK_NUMPAD3,     0 },
	{ ITEM_ID_0_PAD,        DIK_NUMPAD0,        VK_NUMPAD0,     0 },
	{ ITEM_ID_DEL_PAD,      DIK_DECIMAL,        VK_DECIMAL,     0 },
	{ ITEM_ID_F11,          DIK_F11,            VK_F11,         0 },
	{ ITEM_ID_F12,          DIK_F12,            VK_F12,         0 },
	{ ITEM_ID_F13,          DIK_F13,            VK_F13,         0 },
	{ ITEM_ID_F14,          DIK_F14,            VK_F14,         0 },
	{ ITEM_ID_F15,          DIK_F15,            VK_F15,         0 },
	{ ITEM_ID_ENTER_PAD,    DIK_NUMPADENTER,    VK_RETURN,      0 },
	{ ITEM_ID_RCONTROL,     DIK_RCONTROL,       VK_RCONTROL,    0 },
	{ ITEM_ID_SLASH_PAD,    DIK_DIVIDE,         VK_DIVIDE,      0 },
	{ ITEM_ID_PRTSCR,       DIK_SYSRQ,          0,              0 },
	{ ITEM_ID_RALT,         DIK_RMENU,          VK_RMENU,       0 },
	{ ITEM_ID_HOME,         DIK_HOME,           VK_HOME,        0 },
	{ ITEM_ID_UP,           DIK_UP,             VK_UP,          0 },
	{ ITEM_ID_PGUP,         DIK_PRIOR,          VK_PRIOR,       0 },
	{ ITEM_ID_LEFT,         DIK_LEFT,           VK_LEFT,        0 },
	{ ITEM_ID_RIGHT,        DIK_RIGHT,          VK_RIGHT,       0 },
	{ ITEM_ID_END,          DIK_END,            VK_END,         0 },
	{ ITEM_ID_DOWN,         DIK_DOWN,           VK_DOWN,        0 },
	{ ITEM_ID_PGDN,         DIK_NEXT,           VK_NEXT,        0 },
	{ ITEM_ID_INSERT,       DIK_INSERT,         VK_INSERT,      0 },
	{ ITEM_ID_DEL,          DIK_DELETE,         VK_DELETE,      0 },
	{ ITEM_ID_LWIN,         DIK_LWIN,           VK_LWIN,        0 },
	{ ITEM_ID_RWIN,         DIK_RWIN,           VK_RWIN,        0 },
	{ ITEM_ID_MENU,         DIK_APPS,           VK_APPS,        0 },
	{ ITEM_ID_PAUSE,        DIK_PAUSE,          VK_PAUSE,       0 },
	{ ITEM_ID_CANCEL,       0,                  VK_CANCEL,      0 },

	// New keys introduced in Windows 2000. These have no MAME codes to
	// preserve compatibility with old config files that may refer to them
	// as e.g. FORWARD instead of e.g. KEYCODE_WEBFORWARD. They need table
	// entries anyway because otherwise they aren't recognized when
	// GetAsyncKeyState polling is used (as happens currently when MAME is
	// paused). Some codes are missing because the mapping to vkey codes
	// isn't clear, and MapVirtualKey is no help.

	{ ITEM_ID_OTHER_SWITCH, DIK_MUTE,           VK_VOLUME_MUTE,         0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_VOLUMEDOWN,     VK_VOLUME_DOWN,         0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_VOLUMEUP,       VK_VOLUME_UP,           0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_WEBHOME,        VK_BROWSER_HOME,        0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_WEBSEARCH,      VK_BROWSER_SEARCH,      0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_WEBFAVORITES,   VK_BROWSER_FAVORITES,   0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_WEBREFRESH,     VK_BROWSER_REFRESH,     0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_WEBSTOP,        VK_BROWSER_STOP,        0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_WEBFORWARD,     VK_BROWSER_FORWARD,     0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_WEBBACK,        VK_BROWSER_BACK,        0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_MAIL,           VK_LAUNCH_MAIL,         0 },
	{ ITEM_ID_OTHER_SWITCH, DIK_MEDIASELECT,    VK_LAUNCH_MEDIA_SELECT, 0 },
};


//============================================================
//  INLINE FUNCTIONS
//============================================================

INLINE void poll_if_necessary(running_machine &machine)
{
	// make sure we poll at least once every 1/4 second
	if (GetTickCount() > last_poll + 1000 / 4)
		wininput_poll(machine);
}


INLINE input_item_id keyboard_map_scancode_to_itemid(int scancode)
{
	int tablenum;

	// scan the table for a match
	for (tablenum = 0; tablenum < ARRAY_LENGTH(win_key_trans_table); tablenum++)
		if (win_key_trans_table[tablenum][DI_KEY] == scancode)
			return (input_item_id)win_key_trans_table[tablenum][MAME_KEY];

	// default to an "other" switch
	return ITEM_ID_OTHER_SWITCH;
}


INLINE INT32 normalize_absolute_axis(INT32 raw, INT32 rawmin, INT32 rawmax)
{
	INT32 center = (rawmax + rawmin) / 2;

	// make sure we have valid data
	if (rawmin >= rawmax)
		return raw;

	// above center
	if (raw >= center)
	{
		INT32 result = (INT64)(raw - center) * (INT64)INPUT_ABSOLUTE_MAX / (INT64)(rawmax - center);
		return MIN(result, INPUT_ABSOLUTE_MAX);
	}

	// below center
	else
	{
		INT32 result = -((INT64)(center - raw) * (INT64)-INPUT_ABSOLUTE_MIN / (INT64)(center - rawmin));
		return MAX(result, INPUT_ABSOLUTE_MIN);
	}
}



//============================================================
//  wininput_init
//============================================================

void wininput_init(running_machine &machine)
{
	// we need pause and exit callbacks
	machine.add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(FUNC(wininput_pause), &machine));
	machine.add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(FUNC(wininput_resume), &machine));
	machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(wininput_exit), &machine));

	// allocate a lock for input synchronizations, since messages sometimes come from another thread
	input_lock = osd_lock_alloc();
	assert_always(input_lock != NULL, "Failed to allocate input_lock");

	// decode the options
	lightgun_shared_axis_mode = downcast<windows_options &>(machine.options()).dual_lightgun();

	// initialize RawInput and DirectInput (RawInput first so we can fall back)
	rawinput_init(machine);
	dinput_init(machine);
	win32_init(machine);

	// poll once to get the initial states
	input_enabled = TRUE;
	wininput_poll(machine);
}


//============================================================
//  wininput_pause
//============================================================

static void wininput_pause(running_machine &machine)
{
	// keep track of the paused state
	input_paused = true;
}

static void wininput_resume(running_machine &machine)
{
	// keep track of the paused state
	input_paused = false;
}


//============================================================
//  wininput_exit
//============================================================

static void wininput_exit(running_machine &machine)
{
	// acquire the lock and turn off input (this ensures everyone is done)
	osd_lock_acquire(input_lock);
	input_enabled = FALSE;
	osd_lock_release(input_lock);

	// free the lock
	osd_lock_free(input_lock);
}


//============================================================
//  wininput_poll
//============================================================

void wininput_poll(running_machine &machine)
{
	int hasfocus = winwindow_has_focus() && input_enabled;

	// ignore if not enabled
	if (input_enabled)
	{
		// remember when this happened
		last_poll = GetTickCount();

		// periodically process events, in case they're not coming through
		// this also will make sure the mouse state is up-to-date
		winwindow_process_events_periodic(machine);

		// track if mouse/lightgun is enabled, for mouse hiding purposes
		mouse_enabled = machine.input().device_class(DEVICE_CLASS_MOUSE).enabled();
		lightgun_enabled = machine.input().device_class(DEVICE_CLASS_LIGHTGUN).enabled();
	}

	// poll all of the devices
	if (hasfocus)
	{
		device_list_poll_devices(keyboard_list);
		device_list_poll_devices(mouse_list);
		device_list_poll_devices(lightgun_list);
		device_list_poll_devices(joystick_list);
	}
	else
	{
		device_list_reset_devices(keyboard_list);
		device_list_reset_devices(mouse_list);
		device_list_reset_devices(lightgun_list);
		device_list_reset_devices(joystick_list);
	}
}


//============================================================
//  wininput_should_hide_mouse
//============================================================

int wininput_should_hide_mouse(void)
{
	// if we are paused or disabled, no
	if (input_paused || !input_enabled)
		return FALSE;

	// if neither mice nor lightguns enabled in the core, then no
	if (!mouse_enabled && !lightgun_enabled)
		return FALSE;

	// if the window has a menu, no
	if (win_window_list != NULL && win_has_menu(win_window_list))
		return FALSE;

	// otherwise, yes
	return TRUE;
}


//============================================================
//  wininput_handle_mouse_button
//============================================================

BOOL wininput_handle_mouse_button(int button, int down, int x, int y)
{
	device_info *devinfo;

	// ignore if not enabled
	if (!input_enabled)
		return FALSE;

	// only need this for shared axis hack
	if (!lightgun_shared_axis_mode || button >= 4)
		return FALSE;

	// choose a device based on the button
	devinfo = lightgun_list;
	if (button >= 2 && devinfo != NULL)
	{
		button -= 2;
		devinfo = devinfo->next;
	}

	// take the lock
	osd_lock_acquire(input_lock);

	// set the button state
	devinfo->mouse.state.rgbButtons[button] = down ? 0x80 : 0x00;
	if (down)
	{
		RECT client_rect;
		POINT mousepos;

		// get the position relative to the window
		GetClientRect(win_window_list->hwnd, &client_rect);
		mousepos.x = x;
		mousepos.y = y;
		ScreenToClient(win_window_list->hwnd, &mousepos);

		// convert to absolute coordinates
		devinfo->mouse.state.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right);
		devinfo->mouse.state.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom);
	}

	// release the lock
	osd_lock_release(input_lock);
	return TRUE;
}


//============================================================
//  wininput_handle_raw
//============================================================

BOOL wininput_handle_raw(HANDLE device)
{
	BYTE small_buffer[4096];
	LPBYTE data = small_buffer;
	BOOL result = FALSE;
	int size;

	// ignore if not enabled
	if (!input_enabled)
		return result;

	// determine the size of databuffer we need
	if ((*get_rawinput_data)((HRAWINPUT)device, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) != 0)
		return result;

	// if necessary, allocate a temporary buffer and fetch the data
	if (size > sizeof(small_buffer))
	{
		data = global_alloc_array(BYTE, size);
		if (data == NULL)
			return result;
	}

	// fetch the data and process the appropriate message types
	result = (*get_rawinput_data)((HRAWINPUT)device, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER));
	if (result != 0)
	{
		RAWINPUT *input = (RAWINPUT *)data;

		// handle keyboard input
		if (input->header.dwType == RIM_TYPEKEYBOARD)
		{
			osd_lock_acquire(input_lock);
			rawinput_keyboard_update(input->header.hDevice, &input->data.keyboard);
			osd_lock_release(input_lock);
			result = TRUE;
		}

		// handle mouse input
		else if (input->header.dwType == RIM_TYPEMOUSE)
		{
			osd_lock_acquire(input_lock);
			rawinput_mouse_update(input->header.hDevice, &input->data.mouse);
			osd_lock_release(input_lock);
			result = TRUE;
		}
	}

	// free the temporary buffer and return the result
	if (data != small_buffer)
		global_free(data);
	return result;
}


//============================================================
//  wininput_vkey_for_mame_code
//============================================================

int wininput_vkey_for_mame_code(input_code code)
{
	// only works for keyboard switches
	if (code.device_class() == DEVICE_CLASS_KEYBOARD && code.item_class() == ITEM_CLASS_SWITCH)
	{
		input_item_id id = code.item_id();
		int tablenum;

		// scan the table for a match
		for (tablenum = 0; tablenum < ARRAY_LENGTH(win_key_trans_table); tablenum++)
			if (win_key_trans_table[tablenum][MAME_KEY] == id)
				return win_key_trans_table[tablenum][VIRTUAL_KEY];
	}
	return 0;
}


//============================================================
//  customize_input_type_list
//============================================================

void windows_osd_interface::customize_input_type_list(simple_list<input_type_entry> &typelist)
{
	input_type_entry *entry;

	// loop over the defaults
	for (entry = typelist.first(); entry != NULL; entry = entry->next())
		switch (entry->type())
		{
			// disable the config menu if the ALT key is down
			// (allows ALT-TAB to switch between windows apps)
			case IPT_UI_CONFIGURE:
				entry->defseq(SEQ_TYPE_STANDARD).set(KEYCODE_TAB, input_seq::not_code, KEYCODE_LALT, input_seq::not_code, KEYCODE_RALT);
				break;

			// alt-enter for fullscreen
			case IPT_OSD_1:
				entry->configure_osd("TOGGLE_FULLSCREEN", "Toggle Fullscreen");
				entry->defseq(SEQ_TYPE_STANDARD).set(KEYCODE_LALT, KEYCODE_ENTER);
				break;

			// alt-F12 for fullscreen snap
			case IPT_OSD_2:
				entry->configure_osd("RENDER_SNAP", "Take Rendered Snapshot");
				entry->defseq(SEQ_TYPE_STANDARD).set(KEYCODE_LALT, KEYCODE_F12, input_seq::not_code, KEYCODE_LCONTROL);
				break;

			// alt-F11 for fullscreen video
			case IPT_OSD_3:
				entry->configure_osd("RENDER_AVI", "Record Rendered Video");
				entry->defseq(SEQ_TYPE_STANDARD).set(KEYCODE_LALT, KEYCODE_F11);
				break;

			// ctrl-alt-F12 to toggle post-processing
			case IPT_OSD_4:
				entry->configure_osd("POST_PROCESS", "Toggle Post-Processing");
				entry->defseq(SEQ_TYPE_STANDARD).set(KEYCODE_LALT, KEYCODE_LCONTROL, KEYCODE_F5);
				break;

			// leave everything else alone
			default:
				break;
		}
}


//============================================================
//  device_list_poll_devices
//============================================================

static void device_list_poll_devices(device_info *devlist_head)
{
	device_info *curdev;

	for (curdev = devlist_head; curdev != NULL; curdev = curdev->next)
		if (curdev->poll != NULL)
			(*curdev->poll)(curdev);
}


//============================================================
//  device_list_reset_devices
//============================================================

static void device_list_reset_devices(device_info *devlist_head)
{
	device_info *curdev;

	for (curdev = devlist_head; curdev != NULL; curdev = curdev->next)
		generic_device_reset(curdev);
}


//============================================================
//  generic_device_alloc
//============================================================

static device_info *generic_device_alloc(running_machine &machine, device_info **devlist_head_ptr, const TCHAR *name)
{
	device_info **curdev_ptr;
	device_info *devinfo;

	// allocate memory for the device object
	devinfo = global_alloc_clear(device_info(machine));
	devinfo->head = devlist_head_ptr;

	// allocate a UTF8 copy of the name
	devinfo->name = utf8_from_tstring(name);
	if (devinfo->name == NULL)
		goto error;

	// append us to the list
	for (curdev_ptr = devinfo->head; *curdev_ptr != NULL; curdev_ptr = &(*curdev_ptr)->next) ;
	*curdev_ptr = devinfo;

	return devinfo;

error:
	global_free(devinfo);
	return NULL;
}


//============================================================
//  generic_device_free
//============================================================

static void generic_device_free(device_info *devinfo)
{
	device_info **curdev_ptr;

	// remove us from the list
	for (curdev_ptr = devinfo->head; *curdev_ptr != devinfo && *curdev_ptr != NULL; curdev_ptr = &(*curdev_ptr)->next) ;
	if (*curdev_ptr == devinfo)
		*curdev_ptr = devinfo->next;

	// free the copy of the name if present
	if (devinfo->name != NULL)
		osd_free((void *)devinfo->name);
	devinfo->name = NULL;

	// and now free the info
	global_free(devinfo);
}


//============================================================
//  generic_device_index
//============================================================

static int generic_device_index(device_info *devlist_head, device_info *devinfo)
{
	int index = 0;
	while (devlist_head != NULL)
	{
		if (devlist_head == devinfo)
			return index;
		index++;
		devlist_head = devlist_head->next;
	}
	return -1;
}


//============================================================
//  generic_device_reset
//============================================================

static void generic_device_reset(device_info *devinfo)
{
	// keyboard case
	if (devinfo->head == &keyboard_list)
		memset(devinfo->keyboard.state, 0, sizeof(devinfo->keyboard.state));

	// mouse/lightgun case
	else if (devinfo->head == &mouse_list || devinfo->head == &lightgun_list)
		memset(&devinfo->mouse.state, 0, sizeof(devinfo->mouse.state));

	// joystick case
	else if (devinfo->head == &joystick_list)
	{
		int povnum;

		memset(&devinfo->joystick.state, 0, sizeof(devinfo->joystick.state));
		for (povnum = 0; povnum < ARRAY_LENGTH(devinfo->joystick.state.rgdwPOV); povnum++)
			devinfo->joystick.state.rgdwPOV[povnum] = 0xffff;
	}
}


//============================================================
//  generic_button_get_state
//============================================================

static INT32 generic_button_get_state(void *device_internal, void *item_internal)
{
	device_info *devinfo = (device_info *)device_internal;
	BYTE *itemdata = (BYTE *)item_internal;

	// return the current state
	poll_if_necessary(devinfo->machine());
	return *itemdata >> 7;
}


//============================================================
//  generic_axis_get_state
//============================================================

static INT32 generic_axis_get_state(void *device_internal, void *item_internal)
{
	device_info *devinfo = (device_info *)device_internal;
	LONG *axisdata = (LONG *)item_internal;

	// return the current state
	poll_if_necessary(devinfo->machine());
	return *axisdata;
}


//============================================================
//  win32_init
//============================================================

static void win32_init(running_machine &machine)
{
	int gunnum;

	// we don't need any initialization unless we are using shared axis mode for lightguns
	if (!lightgun_shared_axis_mode)
		return;

	// we need an exit callback
	machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(win32_exit), &machine));

	// allocate two lightgun devices
	for (gunnum = 0; gunnum < 2; gunnum++)
	{
		static const TCHAR *const gun_names[] = { TEXT("Shared Axis Gun 1"), TEXT("Shared Axis Gun 2") };
		device_info *devinfo;
		int axisnum, butnum;

		// allocate a device
		devinfo = generic_device_alloc(machine, &lightgun_list, gun_names[gunnum]);
		if (devinfo == NULL)
			break;

		// add the device
		devinfo->device = machine.input().device_class(DEVICE_CLASS_LIGHTGUN).add_device(devinfo->name, devinfo);

		// populate the axes
		for (axisnum = 0; axisnum < 2; axisnum++)
		{
			char *name = utf8_from_tstring(default_axis_name[axisnum]);
			devinfo->device->add_item(name, (input_item_id)(ITEM_ID_XAXIS + axisnum), generic_axis_get_state, &devinfo->mouse.state.lX + axisnum);
			osd_free(name);
		}

		// populate the buttons
		for (butnum = 0; butnum < 2; butnum++)
		{
			char *name = utf8_from_tstring(default_button_name(butnum));
			devinfo->device->add_item(name, (input_item_id)(ITEM_ID_BUTTON1 + butnum), generic_button_get_state, &devinfo->mouse.state.rgbButtons[butnum]);
			osd_free(name);
		}
	}
}


//============================================================
//  win32_exit
//============================================================

static void win32_exit(running_machine &machine)
{
	// skip if we're in shared axis mode
	if (!lightgun_shared_axis_mode)
		return;

	// delete the lightgun devices
	while (lightgun_list != NULL)
		generic_device_free(lightgun_list);
}


//============================================================
//  win32_keyboard_poll
//============================================================

static void win32_keyboard_poll(device_info *devinfo)
{
	int keynum;

	// clear the flag that says we detected a key down via win32
	keyboard_win32_reported_key_down = FALSE;

	// reset the keyboard state and then repopulate
	memset(devinfo->keyboard.state, 0, sizeof(devinfo->keyboard.state));

	// iterate over keys
	for (keynum = 0; keynum < ARRAY_LENGTH(win_key_trans_table); keynum++)
	{
		int vk = win_key_trans_table[keynum][VIRTUAL_KEY];
		if (vk != 0 && (GetAsyncKeyState(vk) & 0x8000) != 0)
		{
			int dik = win_key_trans_table[keynum][DI_KEY];

			// conver the VK code to a scancode (DIK code)
			if (dik != 0)
				devinfo->keyboard.state[dik] = 0x80;

			// set this flag so that we continue to use win32 until all keys are up
			keyboard_win32_reported_key_down = TRUE;
		}
	}
}


//============================================================
//  win32_lightgun_poll
//============================================================

static void win32_lightgun_poll(device_info *devinfo)
{
	INT32 xpos = 0, ypos = 0;
	POINT mousepos;

	// if we are using the shared axis hack, the data is updated via Windows messages only
	if (lightgun_shared_axis_mode)
		return;

	// get the cursor position and transform into final results
	GetCursorPos(&mousepos);
	if (win_window_list != NULL)
	{
		RECT client_rect;

		// get the position relative to the window
		GetClientRect(win_window_list->hwnd, &client_rect);
		ScreenToClient(win_window_list->hwnd, &mousepos);

		// convert to absolute coordinates
		xpos = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right);
		ypos = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom);
	}

	// update the X/Y positions
	devinfo->mouse.state.lX = xpos;
	devinfo->mouse.state.lY = ypos;
}


//============================================================
//  dinput_init
//============================================================

static void dinput_init(running_machine &machine)
{
	HRESULT result;
#if DIRECTINPUT_VERSION >= 0x800
	int didevtype_keyboard = DI8DEVCLASS_KEYBOARD;
	int didevtype_mouse = DI8DEVCLASS_POINTER;
	int didevtype_joystick = DI8DEVCLASS_GAMECTRL;

	dinput_version = DIRECTINPUT_VERSION;
	result = DirectInput8Create(GetModuleHandle(NULL), dinput_version, IID_IDirectInput8, (void **)&dinput, NULL);
	if (result != DI_OK)
	{
		dinput_version = 0;
		return;
	}
#else
	int didevtype_keyboard = DIDEVTYPE_KEYBOARD;
	int didevtype_mouse = DIDEVTYPE_MOUSE;
	int didevtype_joystick = DIDEVTYPE_JOYSTICK;

	// first attempt to initialize DirectInput at the current version
	dinput_version = DIRECTINPUT_VERSION;
	result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL);
	if (result != DI_OK)
	{
		// if that fails, try version 5
		dinput_version = 0x0500;
		result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL);
		if (result != DI_OK)
		{
			// if that fails, try version 3
			dinput_version = 0x0300;
			result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL);
			if (result != DI_OK)
			{
				dinput_version = 0;
				return;
			}
		}
	}
#endif

	mame_printf_verbose("DirectInput: Using DirectInput %d\n", dinput_version >> 8);

	// we need an exit callback
	machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(dinput_exit), &machine));

	// initialize keyboard devices, but only if we don't have any yet
	if (keyboard_list == NULL)
	{
		// enumerate the ones we have
		result = IDirectInput_EnumDevices(dinput, didevtype_keyboard, dinput_keyboard_enum, &machine, DIEDFL_ATTACHEDONLY);
		if (result != DI_OK)
			fatalerror("DirectInput: Unable to enumerate keyboards (result=%08X)\n", (UINT32)result);
	}

	// initialize mouse & lightgun devices, but only if we don't have any yet
	if (mouse_list == NULL)
	{
		// enumerate the ones we have
		result = IDirectInput_EnumDevices(dinput, didevtype_mouse, dinput_mouse_enum, &machine, DIEDFL_ATTACHEDONLY);
		if (result != DI_OK)
			fatalerror("DirectInput: Unable to enumerate mice (result=%08X)\n", (UINT32)result);
	}

	// initialize joystick devices
	result = IDirectInput_EnumDevices(dinput, didevtype_joystick, dinput_joystick_enum, &machine, DIEDFL_ATTACHEDONLY);
	if (result != DI_OK)
		fatalerror("DirectInput: Unable to enumerate joysticks (result=%08X)\n", (UINT32)result);
}


//============================================================
//  dinput_exit
//============================================================

static void dinput_exit(running_machine &machine)
{
	// release all our devices
	while (joystick_list != NULL && joystick_list->dinput.device != NULL)
		dinput_device_release(joystick_list);
	while (lightgun_list != NULL)
		generic_device_free(lightgun_list);
	while (mouse_list != NULL && mouse_list->dinput.device != NULL)
		dinput_device_release(mouse_list);
	while (keyboard_list != NULL && keyboard_list->dinput.device != NULL)
		dinput_device_release(keyboard_list);

	// release DirectInput
	if (dinput != NULL)
		IDirectInput_Release(dinput);
	dinput = NULL;
}


//============================================================
//  dinput_set_dword_property
//============================================================

static HRESULT dinput_set_dword_property(LPDIRECTINPUTDEVICE device, REFGUID property_guid, DWORD object, DWORD how, DWORD value)
{
	DIPROPDWORD dipdw;

	dipdw.diph.dwSize       = sizeof(dipdw);
	dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
	dipdw.diph.dwObj        = object;
	dipdw.diph.dwHow        = how;
	dipdw.dwData            = value;

	return IDirectInputDevice_SetProperty(device, property_guid, &dipdw.diph);
}


//============================================================
//  dinput_device_create
//============================================================

static device_info *dinput_device_create(running_machine &machine, device_info **devlist_head_ptr, LPCDIDEVICEINSTANCE instance, LPCDIDATAFORMAT format1, LPCDIDATAFORMAT format2, DWORD cooperative_level)
{
	device_info *devinfo;
	HRESULT result;

	// allocate memory for the device object
	devinfo = generic_device_alloc(machine, devlist_head_ptr, instance->tszInstanceName);

	// attempt to create a device
	result = IDirectInput_CreateDevice(dinput, WRAP_REFIID(instance->guidInstance), &devinfo->dinput.device, NULL);
	if (result != DI_OK)
		goto error;

	// try to get a version 2 device for it
	result = IDirectInputDevice_QueryInterface(devinfo->dinput.device, WRAP_REFIID(IID_IDirectInputDevice2), (void **)&devinfo->dinput.device2);
	if (result != DI_OK)
		devinfo->dinput.device2 = NULL;

	// get the caps
	devinfo->dinput.caps.dwSize = STRUCTSIZE(DIDEVCAPS);
	result = IDirectInputDevice_GetCapabilities(devinfo->dinput.device, &devinfo->dinput.caps);
	if (result != DI_OK)
		goto error;

	// attempt to set the data format
	devinfo->dinput.format = format1;
	result = IDirectInputDevice_SetDataFormat(devinfo->dinput.device, devinfo->dinput.format);
	if (result != DI_OK)
	{
		// use the secondary format if available
		if (format2 != NULL)
		{
			devinfo->dinput.format = format2;
			result = IDirectInputDevice_SetDataFormat(devinfo->dinput.device, devinfo->dinput.format);
		}
		if (result != DI_OK)
			goto error;
	}

	// set the cooperative level
	result = IDirectInputDevice_SetCooperativeLevel(devinfo->dinput.device, win_window_list->hwnd, cooperative_level);
	if (result != DI_OK)
		goto error;
	return devinfo;

error:
	dinput_device_release(devinfo);
	return NULL;
}


//============================================================
//  dinput_device_release
//============================================================

static void dinput_device_release(device_info *devinfo)
{
	// release the version 2 device if present
	if (devinfo->dinput.device2 != NULL)
		IDirectInputDevice_Release(devinfo->dinput.device2);
	devinfo->dinput.device2 = NULL;

	// release the regular device if present
	if (devinfo->dinput.device != NULL)
		IDirectInputDevice_Release(devinfo->dinput.device);
	devinfo->dinput.device = NULL;

	// free the item list
	generic_device_free(devinfo);
}


//============================================================
//  dinput_device_item_name
//============================================================

static char *dinput_device_item_name(device_info *devinfo, int offset, const TCHAR *defstring, const TCHAR *suffix)
{
	DIDEVICEOBJECTINSTANCE instance = { 0 };
	const TCHAR *namestring = instance.tszName;
	TCHAR *combined;
	HRESULT result;
	char *utf8;

	// query the key name
	instance.dwSize = STRUCTSIZE(DIDEVICEOBJECTINSTANCE);
	result = IDirectInputDevice_GetObjectInfo(devinfo->dinput.device, &instance, offset, DIPH_BYOFFSET);

	// if we got an error and have no default string, just return NULL
	if (result != DI_OK)
	{
		if (defstring == NULL)
			return NULL;
		namestring = defstring;
	}

	// if no suffix, return as-is
	if (suffix == NULL)
		return utf8_from_tstring(namestring);

	// otherwise, allocate space to add the suffix
	combined = global_alloc_array(TCHAR, _tcslen(namestring) + 1 + _tcslen(suffix) + 1);
	_tcscpy(combined, namestring);
	_tcscat(combined, TEXT(" "));
	_tcscat(combined, suffix);

	// convert to UTF8, free the temporary string, and return
	utf8 = utf8_from_tstring(combined);
	global_free(combined);
	return utf8;
}


//============================================================
//  dinput_device_poll
//============================================================

static HRESULT dinput_device_poll(device_info *devinfo)
{
	HRESULT result;

	// first poll the device, then get the state
	if (devinfo->dinput.device2 != NULL)
		IDirectInputDevice2_Poll(devinfo->dinput.device2);
	result = IDirectInputDevice_GetDeviceState(devinfo->dinput.device, devinfo->dinput.format->dwDataSize, &devinfo->joystick.state);

	// handle lost inputs here
	if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED)
	{
		result = IDirectInputDevice_Acquire(devinfo->dinput.device);
		if (result == DI_OK)
			result = IDirectInputDevice_GetDeviceState(devinfo->dinput.device, devinfo->dinput.format->dwDataSize, &devinfo->joystick.state);
	}

	return result;
}


//============================================================
//  dinput_keyboard_enum
//============================================================

static BOOL CALLBACK dinput_keyboard_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref)
{
	running_machine &machine = *(running_machine *)ref;
	device_info *devinfo;
	int keynum;

	// allocate and link in a new device
	devinfo = dinput_device_create(machine, &keyboard_list, instance, &c_dfDIKeyboard, NULL, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	if (devinfo == NULL)
		goto exit;

	// add the device
	devinfo->device = machine.input().device_class(DEVICE_CLASS_KEYBOARD).add_device(devinfo->name, devinfo);
	devinfo->poll = dinput_keyboard_poll;

	// populate it
	for (keynum = 0; keynum < MAX_KEYS; keynum++)
	{
		input_item_id itemid = keyboard_map_scancode_to_itemid(keynum);
		TCHAR defname[20];
		char *name;

		// generate/fetch the name
		_sntprintf(defname, ARRAY_LENGTH(defname), TEXT("Scan%03d"), keynum);
		name = dinput_device_item_name(devinfo, keynum, defname, NULL);

		// add the item to the device
		devinfo->device->add_item(name, itemid, generic_button_get_state, &devinfo->keyboard.state[keynum]);
		osd_free(name);
	}

exit:
	return DIENUM_CONTINUE;
}


//============================================================
//  dinput_keyboard_poll
//============================================================

static void dinput_keyboard_poll(device_info *devinfo)
{
	HRESULT result = dinput_device_poll(devinfo);

	// for the first device, if we errored, or if we previously reported win32 keys,
	// then ignore the dinput state and poll using win32
	if (devinfo == keyboard_list && (result != DI_OK || keyboard_win32_reported_key_down))
		win32_keyboard_poll(devinfo);
}


//============================================================
//  dinput_mouse_enum
//============================================================

static BOOL CALLBACK dinput_mouse_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref)
{
	device_info *devinfo, *guninfo = NULL;
	running_machine &machine = *(running_machine *)ref;
	int axisnum, butnum;
	HRESULT result;

	// allocate and link in a new device
	devinfo = dinput_device_create(machine, &mouse_list, instance, &c_dfDIMouse2, &c_dfDIMouse, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	if (devinfo == NULL)
		goto exit;

	// allocate a second device for the gun (unless we are using the shared axis mode)
	// we only support a single gun in dinput mode, so only add one
	if (!lightgun_shared_axis_mode && devinfo == mouse_list)
	{
		guninfo = generic_device_alloc(machine, &lightgun_list, instance->tszInstanceName);
		if (guninfo == NULL)
			goto exit;
	}

	// set relative mode on the mouse device
	result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_REL);
	if (result != DI_OK && result != DI_PROPNOEFFECT)
	{
		mame_printf_error("DirectInput: Unable to set relative mode for mouse %d (%s)\n", generic_device_index(mouse_list, devinfo), devinfo->name);
		goto error;
	}

	// add the device
	devinfo->device = machine.input().device_class(DEVICE_CLASS_MOUSE).add_device(devinfo->name, devinfo);
	devinfo->poll = dinput_mouse_poll;
	if (guninfo != NULL)
	{
		guninfo->device = machine.input().device_class(DEVICE_CLASS_LIGHTGUN).add_device(guninfo->name, guninfo);
		guninfo->poll = win32_lightgun_poll;
	}

	// cap the number of axes and buttons based on the format
	devinfo->dinput.caps.dwAxes = MIN(devinfo->dinput.caps.dwAxes, 3);
	devinfo->dinput.caps.dwButtons = MIN(devinfo->dinput.caps.dwButtons, (devinfo->dinput.format == &c_dfDIMouse) ? 4 : 8);

	// populate the axes
	for (axisnum = 0; axisnum < devinfo->dinput.caps.dwAxes; axisnum++)
	{
		char *name = dinput_device_item_name(devinfo, offsetof(DIMOUSESTATE, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], NULL);

		// add to the mouse device and optionally to the gun device as well
		devinfo->device->add_item(name, (input_item_id)(ITEM_ID_XAXIS + axisnum), generic_axis_get_state, &devinfo->mouse.state.lX + axisnum);
		if (guninfo != NULL && axisnum < 2)
			guninfo->device->add_item(name, (input_item_id)(ITEM_ID_XAXIS + axisnum), generic_axis_get_state, &guninfo->mouse.state.lX + axisnum);

		osd_free(name);
	}

	// populate the buttons
	for (butnum = 0; butnum < devinfo->dinput.caps.dwButtons; butnum++)
	{
		FPTR offset = (FPTR)(&((DIMOUSESTATE *)NULL)->rgbButtons[butnum]);
		char *name = dinput_device_item_name(devinfo, offset, default_button_name(butnum), NULL);

		// add to the mouse device and optionally to the gun device as well
		// note that the gun device points to the mouse buttons rather than its own
		devinfo->device->add_item(name, (input_item_id)(ITEM_ID_BUTTON1 + butnum), generic_button_get_state, &devinfo->mouse.state.rgbButtons[butnum]);
		if (guninfo != NULL)
			guninfo->device->add_item(name, (input_item_id)(ITEM_ID_BUTTON1 + butnum), generic_button_get_state, &devinfo->mouse.state.rgbButtons[butnum]);

		osd_free(name);
	}

exit:
	return DIENUM_CONTINUE;

error:
	if (guninfo != NULL)
		generic_device_free(guninfo);
	if (devinfo != NULL)
		dinput_device_release(devinfo);
	goto exit;
}


//============================================================
//  dinput_mouse_poll
//============================================================

static void dinput_mouse_poll(device_info *devinfo)
{
	// poll
	dinput_device_poll(devinfo);

	// scale the axis data
	devinfo->mouse.state.lX *= INPUT_RELATIVE_PER_PIXEL;
	devinfo->mouse.state.lY *= INPUT_RELATIVE_PER_PIXEL;
	devinfo->mouse.state.lZ *= INPUT_RELATIVE_PER_PIXEL;
}


//============================================================
//  dinput_joystick_enum
//============================================================

static BOOL CALLBACK dinput_joystick_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref)
{
	DWORD cooperative_level = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE;
	int axisnum, axiscount, povnum, butnum;
	running_machine &machine = *(running_machine *)ref;
	device_info *devinfo;
	HRESULT result;

	if (win_window_list != NULL && win_has_menu(win_window_list)) {
		cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE;
	}
	// allocate and link in a new device
	devinfo = dinput_device_create(machine, &joystick_list, instance, &c_dfDIJoystick, NULL, cooperative_level);
	if (devinfo == NULL)
		goto exit;

	// set absolute mode
	result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_ABS);
	if (result != DI_OK && result != DI_PROPNOEFFECT)
		mame_printf_warning("DirectInput: Unable to set absolute mode for joystick %d (%s)\n", generic_device_index(joystick_list, devinfo), devinfo->name);

	// turn off deadzone; we do our own calculations
	result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_DEADZONE, 0, DIPH_DEVICE, 0);
	if (result != DI_OK && result != DI_PROPNOEFFECT)
		mame_printf_warning("DirectInput: Unable to reset deadzone for joystick %d (%s)\n", generic_device_index(joystick_list, devinfo), devinfo->name);

	// turn off saturation; we do our own calculations
	result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_SATURATION, 0, DIPH_DEVICE, 10000);
	if (result != DI_OK && result != DI_PROPNOEFFECT)
		mame_printf_warning("DirectInput: Unable to reset saturation for joystick %d (%s)\n", generic_device_index(joystick_list, devinfo), devinfo->name);

	// cap the number of axes, POVs, and buttons based on the format
	devinfo->dinput.caps.dwAxes = MIN(devinfo->dinput.caps.dwAxes, 8);
	devinfo->dinput.caps.dwPOVs = MIN(devinfo->dinput.caps.dwPOVs, 4);
	devinfo->dinput.caps.dwButtons = MIN(devinfo->dinput.caps.dwButtons, 128);

	// add the device
	devinfo->device = machine.input().device_class(DEVICE_CLASS_JOYSTICK).add_device(devinfo->name, devinfo);
	devinfo->poll = dinput_joystick_poll;

	// populate the axes
	for (axisnum = axiscount = 0; axiscount < devinfo->dinput.caps.dwAxes && axisnum < 8; axisnum++)
	{
		DIPROPRANGE dipr;
		char *name;

		// fetch the range of this axis
		dipr.diph.dwSize = sizeof(dipr);
		dipr.diph.dwHeaderSize = sizeof(dipr.diph);
		dipr.diph.dwObj = offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG);
		dipr.diph.dwHow = DIPH_BYOFFSET;
		result = IDirectInputDevice_GetProperty(devinfo->dinput.device, DIPROP_RANGE, &dipr.diph);
		if (result != DI_OK)
			continue;
		devinfo->joystick.rangemin[axisnum] = dipr.lMin;
		devinfo->joystick.rangemax[axisnum] = dipr.lMax;

		// populate the item description as well
		name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], NULL);
		devinfo->device->add_item(name, (input_item_id)(ITEM_ID_XAXIS + axisnum), generic_axis_get_state, &devinfo->joystick.state.lX + axisnum);
		osd_free(name);

		axiscount++;
	}

	// populate the POVs
	for (povnum = 0; povnum < devinfo->dinput.caps.dwPOVs; povnum++)
	{
		char *name;

		// left
		name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("L"));
		devinfo->device->add_item(name, ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, (void *)(FPTR)(povnum * 4 + POVDIR_LEFT));
		osd_free(name);

		// right
		name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("R"));
		devinfo->device->add_item(name, ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, (void *)(FPTR)(povnum * 4 + POVDIR_RIGHT));
		osd_free(name);

		// up
		name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("U"));
		devinfo->device->add_item(name, ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, (void *)(FPTR)(povnum * 4 + POVDIR_UP));
		osd_free(name);

		// down
		name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("D"));
		devinfo->device->add_item(name, ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, (void *)(FPTR)(povnum * 4 + POVDIR_DOWN));
		osd_free(name);
	}

	// populate the buttons
	for (butnum = 0; butnum < devinfo->dinput.caps.dwButtons; butnum++)
	{
		FPTR offset = (FPTR)(&((DIJOYSTATE2 *)NULL)->rgbButtons[butnum]);
		char *name = dinput_device_item_name(devinfo, offset, default_button_name(butnum), NULL);
		devinfo->device->add_item(name, (butnum < 16) ? (input_item_id)(ITEM_ID_BUTTON1 + butnum) : ITEM_ID_OTHER_SWITCH, generic_button_get_state, &devinfo->joystick.state.rgbButtons[butnum]);
		osd_free(name);
	}

exit:
	return DIENUM_CONTINUE;
}


//============================================================
//  dinput_joystick_poll
//============================================================

static void dinput_joystick_poll(device_info *devinfo)
{
	int axisnum;

	// poll the device first
	dinput_device_poll(devinfo);

	// normalize axis values
	for (axisnum = 0; axisnum < 8; axisnum++)
	{
		LONG *axis = (&devinfo->joystick.state.lX) + axisnum;
		*axis = normalize_absolute_axis(*axis, devinfo->joystick.rangemin[axisnum], devinfo->joystick.rangemax[axisnum]);
	}
}


//============================================================
//  dinput_joystick_pov_get_state
//============================================================

static INT32 dinput_joystick_pov_get_state(void *device_internal, void *item_internal)
{
	device_info *devinfo = (device_info *)device_internal;
	int povnum = (FPTR)item_internal / 4;
	int povdir = (FPTR)item_internal % 4;
	INT32 result = 0;
	DWORD pov;

	// get the current state
	poll_if_necessary(devinfo->machine());
	pov = devinfo->joystick.state.rgdwPOV[povnum];

	// if invalid, return 0
	if ((pov & 0xffff) == 0xffff)
		return result;

	// return the current state
	switch (povdir)
	{
		case POVDIR_LEFT:   result = (pov >= 22500 && pov <= 31500);    break;
		case POVDIR_RIGHT:  result = (pov >= 4500 && pov <= 13500);     break;
		case POVDIR_UP:     result = (pov >= 31500 || pov <= 4500);     break;
		case POVDIR_DOWN:   result = (pov >= 13500 && pov <= 22500);    break;
	}
	return result;
}


//============================================================
//  rawinput_init
//============================================================

static void rawinput_init(running_machine &machine)
{
	RAWINPUTDEVICELIST *devlist = NULL;
	int device_count, devnum, regcount;
	RAWINPUTDEVICE reglist[2];
	HMODULE user32;

	// we need pause and exit callbacks
	machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(rawinput_exit), &machine));

	// look in user32 for the raw input APIs
	user32 = LoadLibrary(TEXT("user32.dll"));
	if (user32 == NULL)
		goto error;

	// look up the entry points
	register_rawinput_devices = (register_rawinput_devices_ptr)GetProcAddress(user32, "RegisterRawInputDevices");
	get_rawinput_device_list = (get_rawinput_device_list_ptr)GetProcAddress(user32, "GetRawInputDeviceList");
	get_rawinput_device_info = (get_rawinput_device_info_ptr)GetProcAddress(user32, "GetRawInputDeviceInfo" UNICODE_SUFFIX);
	get_rawinput_data = (get_rawinput_data_ptr)GetProcAddress(user32, "GetRawInputData");
	if (register_rawinput_devices == NULL || get_rawinput_device_list == NULL || get_rawinput_device_info == NULL || get_rawinput_data == NULL)
		goto error;
	mame_printf_verbose("RawInput: APIs detected\n");

	// get the number of devices, allocate a device list, and fetch it
	if ((*get_rawinput_device_list)(NULL, &device_count, sizeof(*devlist)) != 0)
		goto error;
	if (device_count == 0)
		goto error;
	devlist = global_alloc_array(RAWINPUTDEVICELIST, device_count);
	if ((*get_rawinput_device_list)(devlist, &device_count, sizeof(*devlist)) == -1)
		goto error;

	// iterate backwards through devices; new devices are added at the head
	for (devnum = device_count - 1; devnum >= 0; devnum--)
	{
		RAWINPUTDEVICELIST *device = &devlist[devnum];

		// handle keyboards
		if (device->dwType == RIM_TYPEKEYBOARD && !FORCE_DIRECTINPUT)
			rawinput_keyboard_enum(machine, device);

		// handle mice
		else if (device->dwType == RIM_TYPEMOUSE && !FORCE_DIRECTINPUT)
			rawinput_mouse_enum(machine, device);
	}

	// finally, register to receive raw input WM_INPUT messages
	regcount = 0;
	if (keyboard_list != NULL)
	{
		reglist[regcount].usUsagePage = 0x01;
		reglist[regcount].usUsage = 0x06;
		reglist[regcount].dwFlags = RIDEV_INPUTSINK;
		reglist[regcount].hwndTarget = win_window_list->hwnd;
		regcount++;
	}
	if (mouse_list != NULL)
	{
		reglist[regcount].usUsagePage = 0x01;
		reglist[regcount].usUsage = 0x02;
		reglist[regcount].dwFlags = 0;
		reglist[regcount].hwndTarget = win_window_list->hwnd;
		regcount++;
	}

	// if the registration fails, we need to back off
	if (regcount > 0)
		if (!(*register_rawinput_devices)(reglist, regcount, sizeof(reglist[0])))
			goto error;

	global_free(devlist);
	return;

error:
	if (devlist != NULL)
		global_free(devlist);
}


//============================================================
//  rawinput_exit
//============================================================

static void rawinput_exit(running_machine &machine)
{
	// release all our devices
	while (lightgun_list != NULL && lightgun_list->rawinput.device != NULL)
		rawinput_device_release(lightgun_list);
	while (mouse_list != NULL && mouse_list->rawinput.device != NULL)
		rawinput_device_release(mouse_list);
	while (keyboard_list != NULL && keyboard_list->rawinput.device != NULL)
		rawinput_device_release(keyboard_list);
}


//============================================================
//  rawinput_device_create
//============================================================

static device_info *rawinput_device_create(running_machine &machine, device_info **devlist_head_ptr, PRAWINPUTDEVICELIST device)
{
	device_info *devinfo = NULL;
	TCHAR *tname = NULL;
	INT name_length;

	// determine the length of the device name, allocate it, and fetch it
	if ((*get_rawinput_device_info)(device->hDevice, RIDI_DEVICENAME, NULL, &name_length) != 0)
		goto error;
	tname = global_alloc_array(TCHAR, name_length);
	if ((*get_rawinput_device_info)(device->hDevice, RIDI_DEVICENAME, tname, &name_length) == -1)
		goto error;

	// if this is an RDP name, skip it
	if (_tcsstr(tname, TEXT("Root#RDP_")) != NULL)
		goto error;

	// improve the name and then allocate a device
	tname = rawinput_device_improve_name(tname);
	devinfo = generic_device_alloc(machine, devlist_head_ptr, tname);
	global_free(tname);

	// copy the handle
	devinfo->rawinput.device = device->hDevice;
	return devinfo;

error:
	if (tname != NULL)
		global_free(tname);
	if (devinfo != NULL)
		rawinput_device_release(devinfo);
	return NULL;
}


//============================================================
//  rawinput_device_release
//============================================================

static void rawinput_device_release(device_info *devinfo)
{
	// free the item list
	generic_device_free(devinfo);
}


//============================================================
//  rawinput_device_improve_name
//============================================================

static TCHAR *rawinput_device_improve_name(TCHAR *name)
{
	static const TCHAR usbbasepath[] = TEXT("SYSTEM\\CurrentControlSet\\Enum\\USB");
	static const TCHAR basepath[] = TEXT("SYSTEM\\CurrentControlSet\\Enum\\");
	TCHAR *regstring = NULL;
	TCHAR *parentid = NULL;
	TCHAR *regpath = NULL;
	const TCHAR *chsrc;
	HKEY regkey = NULL;
	int usbindex;
	TCHAR *chdst;
	LONG result;

	// The RAW name received is formatted as:
	//   \??\type-id#hardware-id#instance-id#{DeviceClasses-id}
	// XP starts with "\??\"
	// Vista64 starts with "\\?\"

	// ensure the name is something we can handle
	if (_tcsncmp(name, TEXT("\\\\?\\"), 4) != 0 && _tcsncmp(name, TEXT("\\??\\"), 4) != 0)
		return name;

	// allocate a temporary string and concatenate the base path plus the name
	regpath = global_alloc_array(TCHAR, _tcslen(basepath) + 1 + _tcslen(name));
	_tcscpy(regpath, basepath);
	chdst = regpath + _tcslen(regpath);

	// convert all # to \ in the name
	for (chsrc = name + 4; *chsrc != 0; chsrc++)
		*chdst++ = (*chsrc == '#') ? '\\' : *chsrc;
	*chdst = 0;

	// remove the final chunk
	chdst = _tcsrchr(regpath, '\\');
	if (chdst == NULL)
		goto exit;
	*chdst = 0;

	// now try to open the registry key
	result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &regkey);
	if (result != ERROR_SUCCESS)
		goto exit;

	// fetch the device description; if it exists, we are finished
	regstring = reg_query_string(regkey, TEXT("DeviceDesc"));
	if (regstring != NULL)
		goto convert;

	// close this key
	RegCloseKey(regkey);
	regkey = NULL;

	// if the key name does not contain "HID", it's not going to be in the USB tree; give up
	if (_tcsstr(regpath, TEXT("HID")) == NULL)
		goto exit;

	// extract the expected parent ID from the regpath
	parentid = _tcsrchr(regpath, '\\');
	if (parentid == NULL)
		goto exit;
	parentid++;

	// open the USB key
	result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, usbbasepath, 0, KEY_READ, &regkey);
	if (result != ERROR_SUCCESS)
		goto exit;

	// enumerate the USB key
	for (usbindex = 0; result == ERROR_SUCCESS && regstring == NULL; usbindex++)
	{
		TCHAR keyname[MAX_PATH];
		DWORD namelen;

		// get the next enumerated subkey and scan it
		namelen = ARRAY_LENGTH(keyname) - 1;
		result = RegEnumKeyEx(regkey, usbindex, keyname, &namelen, NULL, NULL, NULL, NULL);
		if (result == ERROR_SUCCESS)
		{
			LONG subresult;
			int subindex;
			HKEY subkey;

			// open the subkey
			subresult = RegOpenKeyEx(regkey, keyname, 0, KEY_READ, &subkey);
			if (subresult != ERROR_SUCCESS)
				continue;

			// enumerate the subkey
			for (subindex = 0; subresult == ERROR_SUCCESS && regstring == NULL; subindex++)
			{
				// get the next enumerated subkey and scan it
				namelen = ARRAY_LENGTH(keyname) - 1;
				subresult = RegEnumKeyEx(subkey, subindex, keyname, &namelen, NULL, NULL, NULL, NULL);
				if (subresult == ERROR_SUCCESS)
				{
					TCHAR *endparentid;
					LONG endresult;
					HKEY endkey;

					// open this final key
					endresult = RegOpenKeyEx(subkey, keyname, 0, KEY_READ, &endkey);
					if (endresult != ERROR_SUCCESS)
						continue;

					// do we have a match?
					endparentid = reg_query_string(endkey, TEXT("ParentIdPrefix"));
					if (endparentid != NULL && _tcsncmp(parentid, endparentid, _tcslen(endparentid)) == 0)
						regstring = reg_query_string(endkey, TEXT("DeviceDesc"));

					// free memory and close the key
					if (endparentid != NULL)
						global_free(endparentid);
					RegCloseKey(endkey);
				}
			}

			// close the subkey
			RegCloseKey(subkey);
		}
	}

	// if we didn't find anything, go to the exit
	if (regstring == NULL)
		goto exit;

convert:
	// replace the name with the nicer one
	global_free(name);

	// remove anything prior to the final semicolon
	chsrc = _tcsrchr(regstring, ';');
	if (chsrc != NULL)
		chsrc++;
	else
		chsrc = regstring;
	name = global_alloc_array(TCHAR, _tcslen(chsrc) + 1);
	_tcscpy(name, chsrc);

exit:
	if (regstring != NULL)
		global_free(regstring);
	if (regpath != NULL)
		global_free(regpath);
	if (regkey != NULL)
		RegCloseKey(regkey);

	return name;
}


//============================================================
//  rawinput_keyboard_enum
//============================================================

static void rawinput_keyboard_enum(running_machine &machine, PRAWINPUTDEVICELIST device)
{
	device_info *devinfo;
	int keynum;

	// allocate and link in a new device
	devinfo = rawinput_device_create(machine, &keyboard_list, device);
	if (devinfo == NULL)
		return;

	// add the device
	devinfo->device = machine.input().device_class(DEVICE_CLASS_KEYBOARD).add_device(devinfo->name, devinfo);

	// populate it
	for (keynum = 0; keynum < MAX_KEYS; keynum++)
	{
		input_item_id itemid = keyboard_map_scancode_to_itemid(keynum);
		TCHAR keyname[100];
		char *name;

		// generate the name
		if (GetKeyNameText(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, ARRAY_LENGTH(keyname)) == 0)
			_sntprintf(keyname, ARRAY_LENGTH(keyname), TEXT("Scan%03d"), keynum);
		name = utf8_from_tstring(keyname);

		// add the item to the device
		devinfo->device->add_item(name, itemid, generic_button_get_state, &devinfo->keyboard.state[keynum]);
		osd_free(name);
	}
}


//============================================================
//  rawinput_keyboard_update
//============================================================

static void rawinput_keyboard_update(HANDLE device, RAWKEYBOARD *data)
{
	device_info *devinfo;

	// find the keyboard in the list and process
	for (devinfo = keyboard_list; devinfo != NULL; devinfo = devinfo->next)
		if (devinfo->rawinput.device == device)
		{
			// determine the full DIK-compatible scancode
			UINT8 scancode = (data->MakeCode & 0x7f) | ((data->Flags & RI_KEY_E0) ? 0x80 : 0x00);

			// scancode 0xaa is a special shift code we need to ignore
			if (scancode == 0xaa)
				break;

			// set or clear the key
			if (!(data->Flags & RI_KEY_BREAK))
				devinfo->keyboard.state[scancode] = 0x80;
			else
				devinfo->keyboard.state[scancode] = 0x00;
			break;
		}
}


//============================================================
//  rawinput_mouse_enum
//============================================================

static void rawinput_mouse_enum(running_machine &machine, PRAWINPUTDEVICELIST device)
{
	device_info *devinfo, *guninfo = NULL;
	int axisnum, butnum;

	// allocate and link in a new mouse device
	devinfo = rawinput_device_create(machine, &mouse_list, device);
	if (devinfo == NULL)
		return;
	devinfo->poll = rawinput_mouse_poll;

	// allocate a second device for the gun (unless we are using the shared axis mode)
	if (!lightgun_shared_axis_mode)
	{
		guninfo = rawinput_device_create(machine, &lightgun_list, device);
		assert(guninfo != NULL);
	}

	// add the device
	devinfo->device = machine.input().device_class(DEVICE_CLASS_MOUSE).add_device(devinfo->name, devinfo);
	if (guninfo != NULL)
	{
		guninfo->device = machine.input().device_class(DEVICE_CLASS_LIGHTGUN).add_device(guninfo->name, guninfo);
		guninfo->poll = NULL;
	}

	// populate the axes
	for (axisnum = 0; axisnum < 3; axisnum++)
	{
		char *name = utf8_from_tstring(default_axis_name[axisnum]);

		// add to the mouse device and optionally to the gun device as well
		devinfo->device->add_item(name, (input_item_id)(ITEM_ID_XAXIS + axisnum), generic_axis_get_state, &devinfo->mouse.state.lX + axisnum);
		if (guninfo != NULL && axisnum < 2)
			guninfo->device->add_item(name, (input_item_id)(ITEM_ID_XAXIS + axisnum), generic_axis_get_state, &guninfo->mouse.state.lX + axisnum);

		osd_free(name);
	}

	// populate the buttons
	for (butnum = 0; butnum < 5; butnum++)
	{
		char *name = utf8_from_tstring(default_button_name(butnum));

		// add to the mouse device and optionally to the gun device as well
		devinfo->device->add_item(name, (input_item_id)(ITEM_ID_BUTTON1 + butnum), generic_button_get_state, &devinfo->mouse.state.rgbButtons[butnum]);
		if (guninfo != NULL)
			guninfo->device->add_item(name, (input_item_id)(ITEM_ID_BUTTON1 + butnum), generic_button_get_state, &guninfo->mouse.state.rgbButtons[butnum]);

		osd_free(name);
	}
}


//============================================================
//  rawinput_mouse_update
//============================================================

static void rawinput_mouse_update(HANDLE device, RAWMOUSE *data)
{
	device_info *devlist = (data->usFlags & MOUSE_MOVE_ABSOLUTE) ? lightgun_list : mouse_list;
	device_info *devinfo;

	// find the mouse in the list and process
	for (devinfo = devlist; devinfo != NULL; devinfo = devinfo->next)
		if (devinfo->rawinput.device == device)
		{
			// if we got relative data, update it as a mouse
			if (!(data->usFlags & MOUSE_MOVE_ABSOLUTE))
			{
				devinfo->mouse.raw_x += data->lLastX * INPUT_RELATIVE_PER_PIXEL;
				devinfo->mouse.raw_y += data->lLastY * INPUT_RELATIVE_PER_PIXEL;

				// update zaxis
				if (data->usButtonFlags & RI_MOUSE_WHEEL)
					devinfo->mouse.raw_z += (INT16)data->usButtonData * INPUT_RELATIVE_PER_PIXEL;
			}

			// otherwise, update it as a lightgun
			else
			{
				devinfo->mouse.state.lX = normalize_absolute_axis(data->lLastX, 0, 0xffff);
				devinfo->mouse.state.lY = normalize_absolute_axis(data->lLastY, 0, 0xffff);
			}

			// update the button states; always update the corresponding mouse buttons
			if (data->usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) devinfo->mouse.state.rgbButtons[0] = 0x80;
			if (data->usButtonFlags & RI_MOUSE_BUTTON_1_UP)   devinfo->mouse.state.rgbButtons[0] = 0x00;
			if (data->usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) devinfo->mouse.state.rgbButtons[1] = 0x80;
			if (data->usButtonFlags & RI_MOUSE_BUTTON_2_UP)   devinfo->mouse.state.rgbButtons[1] = 0x00;
			if (data->usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) devinfo->mouse.state.rgbButtons[2] = 0x80;
			if (data->usButtonFlags & RI_MOUSE_BUTTON_3_UP)   devinfo->mouse.state.rgbButtons[2] = 0x00;
			if (data->usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) devinfo->mouse.state.rgbButtons[3] = 0x80;
			if (data->usButtonFlags & RI_MOUSE_BUTTON_4_UP)   devinfo->mouse.state.rgbButtons[3] = 0x00;
			if (data->usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) devinfo->mouse.state.rgbButtons[4] = 0x80;
			if (data->usButtonFlags & RI_MOUSE_BUTTON_5_UP)   devinfo->mouse.state.rgbButtons[4] = 0x00;
			break;
		}
}


//============================================================
//  rawinput_mouse_poll
//============================================================

static void rawinput_mouse_poll(device_info *devinfo)
{
	poll_if_necessary(devinfo->machine());

	// copy the accumulated raw state to the actual state
	osd_lock_acquire(input_lock);
	devinfo->mouse.state.lX = devinfo->mouse.raw_x;
	devinfo->mouse.state.lY = devinfo->mouse.raw_y;
	devinfo->mouse.state.lZ = devinfo->mouse.raw_z;
	devinfo->mouse.raw_x = 0;
	devinfo->mouse.raw_y = 0;
	devinfo->mouse.raw_z = 0;
	osd_lock_release(input_lock);
}


//============================================================
//  reg_query_string
//============================================================

static TCHAR *reg_query_string(HKEY key, const TCHAR *path)
{
	TCHAR *buffer;
	DWORD datalen;
	LONG result;

	// first query to get the length
	result = RegQueryValueEx(key, path, NULL, NULL, NULL, &datalen);
	if (result != ERROR_SUCCESS)
		return NULL;

	// allocate a buffer
	buffer = global_alloc_array(TCHAR, datalen + sizeof(*buffer));
	buffer[datalen / sizeof(*buffer)] = 0;

	// now get the actual data
	result = RegQueryValueEx(key, path, NULL, NULL, (LPBYTE)buffer, &datalen);
	if (result == ERROR_SUCCESS)
		return buffer;

	// otherwise return a NULL buffer
	global_free(buffer);
	return NULL;
}


//============================================================
//  default_button_name
//============================================================

static const TCHAR *default_button_name(int which)
{
	static TCHAR buffer[20];
	_sntprintf(buffer, ARRAY_LENGTH(buffer), TEXT("B%d"), which);
	return buffer;
}


//============================================================
//  default_pov_name
//============================================================

static const TCHAR *default_pov_name(int which)
{
	static TCHAR buffer[20];
	_sntprintf(buffer, ARRAY_LENGTH(buffer), TEXT("POV%d"), which);
	return buffer;
}