// license:BSD-3-Clause // copyright-holders:Olivier Galibert, R. Belmont //============================================================ // // input.c - SDL implementation of MAME input routines // // SDLMAME by Olivier Galibert and R. Belmont // // SixAxis info: left analog is axes 0 & 1, right analog is axes 2 & 3, // analog L2 is axis 12 and analog L3 is axis 13 // //============================================================ // standard sdl header #include "sdlinc.h" #include #include #include #if USE_XINPUT // for xinput #include #include #include #endif // MAME headers #include "emu.h" #include "ui/ui.h" #include "uiinput.h" #include "emuopts.h" // MAMEOS headers #include "input.h" #include "osdsdl.h" #include "window.h" // winnt.h defines this #ifdef DELETE #undef DELETE #endif //============================================================ // PARAMETERS //============================================================ enum { POVDIR_LEFT = 0, POVDIR_RIGHT, POVDIR_UP, POVDIR_DOWN }; #define MAX_KEYS 256 #define MAX_AXES 32 #define MAX_BUTTONS 32 #define MAX_HATS 8 #define MAX_POV 4 #define MAX_DEVMAP_ENTRIES 16 #if (USE_XINPUT) //For xinput #define INVALID_EVENT_TYPE -1 static int motion_type = INVALID_EVENT_TYPE; static int button_press_type = INVALID_EVENT_TYPE; static int button_release_type = INVALID_EVENT_TYPE; static int key_press_type = INVALID_EVENT_TYPE; static int key_release_type = INVALID_EVENT_TYPE; static int proximity_in_type = INVALID_EVENT_TYPE; static int proximity_out_type = INVALID_EVENT_TYPE; #endif //============================================================ // MACROS //============================================================ // introduced in 1.3 #ifndef SDLK_INDEX #define SDLK_INDEX(x) (x) #endif //============================================================ // TYPEDEFS //============================================================ // state information for a keyboard struct keyboard_state { INT32 state[0x3ff]; // must be INT32! INT8 oldkey[MAX_KEYS]; INT8 currkey[MAX_KEYS]; }; // state information for a mouse struct mouse_state { INT32 lX, lY; INT32 buttons[MAX_BUTTONS]; }; // state information for a joystick; DirectInput state must be first element struct joystick_state { SDL_Joystick *device; INT32 axes[MAX_AXES]; INT32 buttons[MAX_BUTTONS]; INT32 hatsU[MAX_HATS], hatsD[MAX_HATS], hatsL[MAX_HATS], hatsR[MAX_HATS]; INT32 balls[MAX_AXES]; }; #if (USE_XINPUT) // state information for a lightgun struct lightgun_state { INT32 lX, lY; INT32 buttons[MAX_BUTTONS]; XID deviceid; //Xinput device id INT32 maxx,maxy; INT32 minx,miny; }; #endif // generic device information struct device_info { // device information device_info ** head; device_info * next; std::string name; // MAME information input_device * device; // device state union { keyboard_state keyboard; mouse_state mouse; joystick_state joystick; #if (USE_XINPUT) lightgun_state lightgun; #endif }; }; //============================================================ // LOCAL VARIABLES //============================================================ // global states static std::mutex input_lock; static UINT8 input_paused; static sdl_window_info * focus_window = NULL; // input buffer - only for SDLMAME_EVENTS_IN_WORKER_THREAD #define MAX_BUF_EVENTS (1000) /* 100 not enough for SDL 1.3 */ static SDL_Event event_buf[MAX_BUF_EVENTS]; static int event_buf_count; // keyboard states static device_info * keyboard_list; // mouse states static UINT8 app_has_mouse_focus; static UINT8 mouse_enabled; static device_info * mouse_list; // lightgun states static UINT8 lightgun_enabled; static device_info * lightgun_list; // joystick states static device_info * joystick_list; // joystick mapper struct device_map_t { struct { char *name; int physical; } map[MAX_DEVMAP_ENTRIES]; int logical[MAX_DEVMAP_ENTRIES]; int initialized; }; static device_map_t joy_map; static device_map_t mouse_map; static device_map_t keyboard_map; #if (USE_XINPUT) static device_map_t lightgun_map; Display *XDisplay; #endif static int sixaxis_mode; //============================================================ // PROTOTYPES //============================================================ // deivce list management static void device_list_reset_devices(device_info *devlist_head); static void device_list_free_devices(device_info **devlist_head); // generic device management static device_info *generic_device_alloc(device_info **devlist_head_ptr, const char *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); static device_info *generic_device_find_index(device_info *devlist_head, int index); //============================================================ // KEYBOARD/JOYSTICK LIST //============================================================ // master keyboard translation table struct kt_table { input_item_id mame_key; INT32 sdl_key; //const char * vkey; //const char * ascii; const char * mame_key_name; char * ui_name; }; #if (SDLMAME_SDL2) #define OSD_SDL_INDEX(x) (x) #define OSD_SDL_INDEX_KEYSYM(keysym) ((keysym)->scancode) #define GET_WINDOW(ev) window_from_id((ev)->windowID) //#define GET_WINDOW(ev) ((ev)->windowID) // FIXME: sdl does not properly report the window for certain OS. #define GET_FOCUS_WINDOW(ev) focus_window //#define GET_FOCUS_WINDOW(ev) window_from_id((ev)->windowID) #define KTT_ENTRY0(MAME, SDL, VK, AS, UI) { ITEM_ID_ ## MAME, SDL_SCANCODE_ ## SDL, "ITEM_ID_" #MAME, (char *) UI } #define KTT_ENTRY1(MAME, SDL) KTT_ENTRY0(MAME, SDL, MAME, MAME, #MAME) // only for reference ... #define KTT_ENTRY2(MAME, SDL) KTT_ENTRY0(MAME, SDL, 0, 0, #MAME) static kt_table sdl_key_trans_table[] = { // MAME key SDL key vkey ascii KTT_ENTRY0( ESC, ESCAPE, 0x1b, 0x1b, "ESC" ), // 0 KTT_ENTRY1( 1, 1 ), // 1 KTT_ENTRY1( 2, 2 ), // 2 KTT_ENTRY1( 3, 3 ), // 3 KTT_ENTRY1( 4, 4 ), // 4 KTT_ENTRY1( 5, 5 ), // 5 KTT_ENTRY1( 6, 6 ), // 6 KTT_ENTRY1( 7, 7 ), // 7 KTT_ENTRY1( 8, 8 ), // 8 KTT_ENTRY1( 9, 9 ), // 9 KTT_ENTRY1( 0, 0 ), // 10 KTT_ENTRY0( MINUS, MINUS, 0xbd, '-', "MINUS" ), // 11 KTT_ENTRY0( EQUALS, EQUALS, 0xbb, '=', "EQUALS" ), // 12 KTT_ENTRY0( BACKSPACE, BACKSPACE, 0x08, 0x08, "BACKSPACE" ), // 13 KTT_ENTRY0( TAB, TAB, 0x09, 0x09, "TAB" ), // 14 KTT_ENTRY1( Q, Q ), // 15 KTT_ENTRY1( W, W ), // 16 KTT_ENTRY1( E, E ), // 17 KTT_ENTRY1( R, R ), // 18 KTT_ENTRY1( T, T ), // 19 KTT_ENTRY1( Y, Y ), // 20 KTT_ENTRY1( U, U ), // 21 KTT_ENTRY1( I, I ), // 22 KTT_ENTRY1( O, O ), // 23 KTT_ENTRY1( P, P ), // 24 KTT_ENTRY0( OPENBRACE, LEFTBRACKET, 0xdb, '[', "OPENBRACE" ), // 25 KTT_ENTRY0( CLOSEBRACE,RIGHTBRACKET, 0xdd, ']', "CLOSEBRACE" ), // 26 KTT_ENTRY0( ENTER, RETURN, 0x0d, 0x0d, "RETURN" ), // 27 KTT_ENTRY2( LCONTROL, LCTRL ), // 28 KTT_ENTRY1( A, A ), // 29 KTT_ENTRY1( S, S ), // 30 KTT_ENTRY1( D, D ), // 31 KTT_ENTRY1( F, F ), // 32 KTT_ENTRY1( G, G ), // 33 KTT_ENTRY1( H, H ), // 34 KTT_ENTRY1( J, J ), // 35 KTT_ENTRY1( K, K ), // 36 KTT_ENTRY1( L, L ), // 37 KTT_ENTRY0( COLON, SEMICOLON, 0xba, ';', "COLON" ), // 38 KTT_ENTRY0( QUOTE, APOSTROPHE, 0xde, '\'', "QUOTE" ), // 39 KTT_ENTRY2( LSHIFT, LSHIFT ), // 40 KTT_ENTRY0( BACKSLASH, BACKSLASH, 0xdc, '\\', "BACKSLASH" ), // 41 KTT_ENTRY1( Z, Z ), // 42 KTT_ENTRY1( X, X ), // 43 KTT_ENTRY1( C, C ), // 44 KTT_ENTRY1( V, V ), // 45 KTT_ENTRY1( B, B ), // 46 KTT_ENTRY1( N, N ), // 47 KTT_ENTRY1( M, M ), // 48 KTT_ENTRY0( COMMA, COMMA, 0xbc, ',', "COMMA" ), // 49 KTT_ENTRY0( STOP, PERIOD, 0xbe, '.', "STOP" ), // 50 KTT_ENTRY0( SLASH, SLASH, 0xbf, '/', "SLASH" ), // 51 KTT_ENTRY2( RSHIFT, RSHIFT ), // 52 KTT_ENTRY0( ASTERISK, KP_MULTIPLY, '*', '*', "ASTERIX" ), // 53 KTT_ENTRY2( LALT, LALT ), // 54 KTT_ENTRY0( SPACE, SPACE, ' ', ' ', "SPACE" ), // 55 KTT_ENTRY2( CAPSLOCK, CAPSLOCK ), // 56 KTT_ENTRY2( F1, F1 ), // 57 KTT_ENTRY2( F2, F2 ), // 58 KTT_ENTRY2( F3, F3 ), // 59 KTT_ENTRY2( F4, F4 ), // 60 KTT_ENTRY2( F5, F5 ), // 61 KTT_ENTRY2( F6, F6 ), // 62 KTT_ENTRY2( F7, F7 ), // 63 KTT_ENTRY2( F8, F8 ), // 64 KTT_ENTRY2( F9, F9 ), // 65 KTT_ENTRY2( F10, F10 ), // 66 KTT_ENTRY2( NUMLOCK, NUMLOCKCLEAR ), // 67 KTT_ENTRY2( SCRLOCK, SCROLLLOCK ), // 68 KTT_ENTRY2( 7_PAD, KP_7 ), // 69 KTT_ENTRY2( 8_PAD, KP_8 ), KTT_ENTRY2( 9_PAD, KP_9 ), KTT_ENTRY2( MINUS_PAD, KP_MINUS ), KTT_ENTRY2( 4_PAD, KP_4 ), KTT_ENTRY2( 5_PAD, KP_5 ), KTT_ENTRY2( 6_PAD, KP_6 ), KTT_ENTRY2( PLUS_PAD, KP_PLUS ), KTT_ENTRY2( 1_PAD, KP_1 ), KTT_ENTRY2( 2_PAD, KP_2 ), KTT_ENTRY2( 3_PAD, KP_3 ), KTT_ENTRY2( 0_PAD, KP_0 ), KTT_ENTRY2( DEL_PAD, KP_PERIOD ), KTT_ENTRY2( F11, F11 ), KTT_ENTRY2( F12, F12 ), KTT_ENTRY2( F13, F13 ), KTT_ENTRY2( F14, F14 ), KTT_ENTRY2( F15, F15 ), KTT_ENTRY2( ENTER_PAD, KP_ENTER ), KTT_ENTRY2( RCONTROL, RCTRL ), KTT_ENTRY2( SLASH_PAD, KP_DIVIDE ), KTT_ENTRY2( PRTSCR, PRINTSCREEN ), KTT_ENTRY2( RALT, RALT ), KTT_ENTRY2( HOME, HOME ), KTT_ENTRY2( UP, UP ), KTT_ENTRY2( PGUP, PAGEUP ), KTT_ENTRY2( LEFT, LEFT ), KTT_ENTRY2( RIGHT, RIGHT ), KTT_ENTRY2( END, END ), KTT_ENTRY2( DOWN, DOWN ), KTT_ENTRY2( PGDN, PAGEDOWN ), KTT_ENTRY2( INSERT, INSERT ), { ITEM_ID_DEL, SDL_SCANCODE_DELETE, "ITEM_ID_DEL", (char *)"DELETE" }, KTT_ENTRY2( LWIN, LGUI ), KTT_ENTRY2( RWIN, RGUI ), KTT_ENTRY2( MENU, MENU ), KTT_ENTRY0( TILDE, GRAVE, 0xc0, '`', "TILDE" ), KTT_ENTRY0( BACKSLASH2, NONUSBACKSLASH, 0xdc, '\\', "BACKSLASH2" ), { ITEM_ID_INVALID } }; #else #define OSD_SDL_INDEX(x) (SDLK_INDEX(x)-SDLK_FIRST) #define OSD_SDL_INDEX_KEYSYM(keysym) (OSD_SDL_INDEX((keysym)->sym)) #define GET_WINDOW(ev) sdl_window_list #define GET_FOCUS_WINDOW(ev) sdl_window_list #define KTT_ENTRY0(MAME, SDL, VK, AS, UI) { ITEM_ID_ ## MAME, SDLK_ ## SDL, "ITEM_ID_" #MAME, (char *) UI } #define KTT_ENTRY1(MAME, SDL) KTT_ENTRY0(MAME, SDL, MAME, MAME, #MAME) // only for reference ... #define KTT_ENTRY2(MAME, SDL) KTT_ENTRY0(MAME, SDL, 0, 0, #MAME) static kt_table sdl_key_trans_table[] = { // MAME key SDL key vkey ascii KTT_ENTRY0( ESC, ESCAPE, 0x1b, 0x1b, "ESC" ), KTT_ENTRY1( 1, 1 ), KTT_ENTRY1( 2, 2 ), KTT_ENTRY1( 3, 3 ), KTT_ENTRY1( 4, 4 ), KTT_ENTRY1( 5, 5 ), KTT_ENTRY1( 6, 6 ), KTT_ENTRY1( 7, 7 ), KTT_ENTRY1( 8, 8 ), KTT_ENTRY1( 9, 9 ), KTT_ENTRY1( 0, 0 ), KTT_ENTRY0( MINUS, MINUS, 0xbd, '-', "MINUS" ), KTT_ENTRY0( EQUALS, EQUALS, 0xbb, '=', "EQUALS" ), KTT_ENTRY0( BACKSPACE, BACKSPACE, 0x08, 0x08, "BACKSPACE" ), KTT_ENTRY0( TAB, TAB, 0x09, 0x09, "TAB" ), KTT_ENTRY1( Q, q ), KTT_ENTRY1( W, w ), KTT_ENTRY1( E, e ), KTT_ENTRY1( R, r ), KTT_ENTRY1( T, t ), KTT_ENTRY1( Y, y ), KTT_ENTRY1( U, u ), KTT_ENTRY1( I, i ), KTT_ENTRY1( O, o ), KTT_ENTRY1( P, p ), KTT_ENTRY0( OPENBRACE, LEFTBRACKET, 0xdb, '[', "OPENBRACE" ), KTT_ENTRY0( CLOSEBRACE,RIGHTBRACKET, 0xdd, ']', "CLOSEBRACE" ), KTT_ENTRY0( ENTER, RETURN, 0x0d, 0x0d, "RETURN" ), KTT_ENTRY2( LCONTROL, LCTRL ), KTT_ENTRY1( A, a ), KTT_ENTRY1( S, s ), KTT_ENTRY1( D, d ), KTT_ENTRY1( F, f ), KTT_ENTRY1( G, g ), KTT_ENTRY1( H, h ), KTT_ENTRY1( J, j ), KTT_ENTRY1( K, k ), KTT_ENTRY1( L, l ), KTT_ENTRY0( COLON, SEMICOLON, 0xba, ';', "COLON" ), KTT_ENTRY0( QUOTE, QUOTE, 0xde, '\'', "QUOTE" ), KTT_ENTRY2( LSHIFT, LSHIFT ), KTT_ENTRY0( BACKSLASH, BACKSLASH, 0xdc, '\\', "BACKSLASH" ), KTT_ENTRY1( Z, z ), KTT_ENTRY1( X, x ), KTT_ENTRY1( C, c ), KTT_ENTRY1( V, v ), KTT_ENTRY1( B, b ), KTT_ENTRY1( N, n ), KTT_ENTRY1( M, m ), KTT_ENTRY0( COMMA, COMMA, 0xbc, ',', "COMMA" ), KTT_ENTRY0( STOP, PERIOD, 0xbe, '.', "STOP" ), KTT_ENTRY0( SLASH, SLASH, 0xbf, '/', "SLASH" ), KTT_ENTRY2( RSHIFT, RSHIFT ), KTT_ENTRY0( ASTERISK, KP_MULTIPLY, '*', '*', "ASTERIX" ), KTT_ENTRY2( LALT, LALT ), KTT_ENTRY0( SPACE, SPACE, ' ', ' ', "SPACE" ), KTT_ENTRY2( CAPSLOCK, CAPSLOCK ), KTT_ENTRY2( F1, F1 ), KTT_ENTRY2( F2, F2 ), KTT_ENTRY2( F3, F3 ), KTT_ENTRY2( F4, F4 ), KTT_ENTRY2( F5, F5 ), KTT_ENTRY2( F6, F6 ), KTT_ENTRY2( F7, F7 ), KTT_ENTRY2( F8, F8 ), KTT_ENTRY2( F9, F9 ), KTT_ENTRY2( F10, F10 ), KTT_ENTRY2( NUMLOCK, NUMLOCK ), KTT_ENTRY2( SCRLOCK, SCROLLOCK ), KTT_ENTRY2( 7_PAD, KP7 ), KTT_ENTRY2( 8_PAD, KP8 ), KTT_ENTRY2( 9_PAD, KP9 ), KTT_ENTRY2( MINUS_PAD, KP_MINUS ), KTT_ENTRY2( 4_PAD, KP4 ), KTT_ENTRY2( 5_PAD, KP5 ), KTT_ENTRY2( 6_PAD, KP6 ), KTT_ENTRY2( PLUS_PAD, KP_PLUS ), KTT_ENTRY2( 1_PAD, KP1 ), KTT_ENTRY2( 2_PAD, KP2 ), KTT_ENTRY2( 3_PAD, KP3 ), KTT_ENTRY2( 0_PAD, KP0 ), KTT_ENTRY2( DEL_PAD, KP_PERIOD ), KTT_ENTRY2( F11, F11 ), KTT_ENTRY2( F12, F12 ), KTT_ENTRY2( F13, F13 ), KTT_ENTRY2( F14, F14 ), KTT_ENTRY2( F15, F15 ), KTT_ENTRY2( ENTER_PAD, KP_ENTER ), KTT_ENTRY2( RCONTROL, RCTRL ), KTT_ENTRY2( SLASH_PAD, KP_DIVIDE ), KTT_ENTRY2( PRTSCR, PRINT ), KTT_ENTRY2( RALT, RALT ), KTT_ENTRY2( HOME, HOME ), KTT_ENTRY2( UP, UP ), KTT_ENTRY2( PGUP, PAGEUP ), KTT_ENTRY2( LEFT, LEFT ), KTT_ENTRY2( RIGHT, RIGHT ), KTT_ENTRY2( END, END ), KTT_ENTRY2( DOWN, DOWN ), KTT_ENTRY2( PGDN, PAGEDOWN ), KTT_ENTRY2( INSERT, INSERT ), { ITEM_ID_DEL, SDLK_DELETE, "ITEM_ID_DEL", (char *)"DELETE" }, KTT_ENTRY2( LWIN, LSUPER ), KTT_ENTRY2( RWIN, RSUPER ), KTT_ENTRY2( MENU, MENU ), KTT_ENTRY0( TILDE, BACKQUOTE, 0xc0, '`', "TILDE" ), KTT_ENTRY0( BACKSLASH2, HASH, 0xdc, '\\', "BACKSLASH2" ), { ITEM_ID_INVALID } }; #endif struct key_lookup_table { int code; const char *name; }; #if (SDLMAME_SDL2) #define KE(x) { SDL_SCANCODE_ ## x, "SDL_SCANCODE_" #x }, #define KE8(A, B, C, D, E, F, G, H) KE(A) KE(B) KE(C) KE(D) KE(E) KE(F) KE(G) KE(H) #define KE7(A, B, C, D, E, F, G) KE(A) KE(B) KE(C) KE(D) KE(E) KE(F) KE(G) #define KE5(A, B, C, D, E) KE(A) KE(B) KE(C) KE(D) KE(E) #define KE3(A, B, C) KE(A) KE(B) KE(C) static key_lookup_table sdl_lookup_table[] = { KE7(UNKNOWN, BACKSPACE, TAB, CLEAR, RETURN, PAUSE, ESCAPE ) KE(SPACE) KE5(COMMA, MINUS, PERIOD, SLASH, 0 ) KE8(1, 2, 3, 4, 5, 6, 7, 8 ) KE3(9, SEMICOLON, EQUALS) KE5(LEFTBRACKET,BACKSLASH, RIGHTBRACKET, A, B ) KE8(C, D, E, F, G, H, I, J ) KE8(K, L, M, N, O, P, Q, R ) KE8(S, T, U, V, W, X, Y, Z ) KE8(DELETE, KP_0, KP_1, KP_2, KP_3, KP_4, KP_5, KP_6 ) KE8(KP_7, KP_8, KP_9, KP_PERIOD, KP_DIVIDE, KP_MULTIPLY,KP_MINUS, KP_PLUS ) KE8(KP_ENTER, KP_EQUALS, UP, DOWN, RIGHT, LEFT, INSERT, HOME ) KE8(END, PAGEUP, PAGEDOWN, F1, F2, F3, F4, F5 ) KE8(F6, F7, F8, F9, F10, F11, F12, F13 ) KE8(F14, F15, NUMLOCKCLEAR, CAPSLOCK, SCROLLLOCK, RSHIFT, LSHIFT, RCTRL ) KE5(LCTRL, RALT, LALT, LGUI, RGUI) KE8(GRAVE, LEFTBRACKET,RIGHTBRACKET, SEMICOLON, APOSTROPHE, BACKSLASH, PRINTSCREEN,MENU ) KE(UNDO) {-1, ""} }; #else #define KE(x) { SDLK_ ## x, "SDLK_" #x }, #define KE8(A, B, C, D, E, F, G, H) KE(A) KE(B) KE(C) KE(D) KE(E) KE(F) KE(G) KE(H) static key_lookup_table sdl_lookup_table[] = { KE8(UNKNOWN, FIRST, BACKSPACE, TAB, CLEAR, RETURN, PAUSE, ESCAPE ) KE8(SPACE, EXCLAIM, QUOTEDBL, HASH, DOLLAR, AMPERSAND, QUOTE, LEFTPAREN ) KE8(RIGHTPAREN, ASTERISK, PLUS, COMMA, MINUS, PERIOD, SLASH, 0 ) KE8(1, 2, 3, 4, 5, 6, 7, 8 ) KE8(9, COLON, SEMICOLON, LESS, EQUALS, GREATER, QUESTION, AT ) KE8(LEFTBRACKET,BACKSLASH, RIGHTBRACKET, CARET, UNDERSCORE, BACKQUOTE, a, b ) KE8(c, d, e, f, g, h, i, j ) KE8(k, l, m, n, o, p, q, r ) KE8(s, t, u, v, w, x, y, z ) KE8(DELETE, WORLD_0, WORLD_1, WORLD_2, WORLD_3, WORLD_4, WORLD_5, WORLD_6 ) KE8(WORLD_7, WORLD_8, WORLD_9, WORLD_10, WORLD_11, WORLD_12, WORLD_13, WORLD_14 ) KE8(WORLD_15, WORLD_16, WORLD_17, WORLD_18, WORLD_19, WORLD_20, WORLD_21, WORLD_22 ) KE8(WORLD_23, WORLD_24, WORLD_25, WORLD_26, WORLD_27, WORLD_28, WORLD_29, WORLD_30 ) KE8(WORLD_31, WORLD_32, WORLD_33, WORLD_34, WORLD_35, WORLD_36, WORLD_37, WORLD_38 ) KE8(WORLD_39, WORLD_40, WORLD_41, WORLD_42, WORLD_43, WORLD_44, WORLD_45, WORLD_46 ) KE8(WORLD_47, WORLD_48, WORLD_49, WORLD_50, WORLD_51, WORLD_52, WORLD_53, WORLD_54 ) KE8(WORLD_55, WORLD_56, WORLD_57, WORLD_58, WORLD_59, WORLD_60, WORLD_61, WORLD_62 ) KE8(WORLD_63, WORLD_64, WORLD_65, WORLD_66, WORLD_67, WORLD_68, WORLD_69, WORLD_70 ) KE8(WORLD_71, WORLD_72, WORLD_73, WORLD_74, WORLD_75, WORLD_76, WORLD_77, WORLD_78 ) KE8(WORLD_79, WORLD_80, WORLD_81, WORLD_82, WORLD_83, WORLD_84, WORLD_85, WORLD_86 ) KE8(WORLD_87, WORLD_88, WORLD_89, WORLD_90, WORLD_91, WORLD_92, WORLD_93, WORLD_94 ) KE8(WORLD_95, KP0, KP1, KP2, KP3, KP4, KP5, KP6 ) KE8(KP7, KP8, KP9, KP_PERIOD, KP_DIVIDE, KP_MULTIPLY,KP_MINUS, KP_PLUS ) KE8(KP_ENTER, KP_EQUALS, UP, DOWN, RIGHT, LEFT, INSERT, HOME ) KE8(END, PAGEUP, PAGEDOWN, F1, F2, F3, F4, F5 ) KE8(F6, F7, F8, F9, F10, F11, F12, F13 ) KE8(F14, F15, NUMLOCK, CAPSLOCK, SCROLLOCK, RSHIFT, LSHIFT, RCTRL ) KE8(LCTRL, RALT, LALT, RMETA, LMETA, LSUPER, RSUPER, MODE ) KE8(COMPOSE, HELP, PRINT, SYSREQ, BREAK, MENU, POWER, EURO ) KE(UNDO) KE(LAST) {-1, ""} }; #endif //============================================================ // INLINE FUNCTIONS //============================================================ static int devmap_leastfree(device_map_t *devmap) { int i; for (i=0;imap[i].name == 0) return i; } return -1; } static char *remove_spaces(running_machine &machine, const char *s) { char *r, *p; static const char *def_name[] = { "Unknown" }; while (*s && *s == ' ') s++; if (strlen(s) == 0) { r = auto_alloc_array(machine, char, strlen((char *)def_name) + 1); strcpy(r, (char *)def_name); return r; } r = auto_alloc_array(machine, char, strlen(s) + 1); p = r; while (*s) { if (*s != ' ') *p++ = *s++; else { while (*s && *s == ' ') s++; if (*s) *p++ = ' '; } } *p = 0; return r; } static void devmap_register(device_map_t *devmap, int physical_idx, char *name) { int found = 0; int stick, i; for (i=0;imap[i].name) == 0 && devmap->map[i].physical < 0) { devmap->map[i].physical = physical_idx; found = 1; devmap->logical[physical_idx] = i; } } if (found == 0) { stick = devmap_leastfree(devmap); devmap->map[stick].physical = physical_idx; devmap->map[stick].name = name; devmap->logical[physical_idx] = stick; } } //============================================================ // init_joymap //============================================================ static void devmap_init(running_machine &machine, device_map_t *devmap, const char *opt, int max_devices, const char *label) { int dev; char defname[20]; assert(max_devices <= MAX_DEVMAP_ENTRIES); for (dev = 0; dev < MAX_DEVMAP_ENTRIES; dev++) { devmap->map[dev].name = (char *)""; devmap->map[dev].physical = -1; devmap->logical[dev] = -1; } devmap->initialized = 0; for (dev = 0; dev < max_devices; dev++) { const char *dev_name; sprintf(defname, "%s%d", opt, dev + 1); dev_name = machine.options().value(defname); if (dev_name && *dev_name && strcmp(dev_name,OSDOPTVAL_AUTO)) { devmap->map[dev].name = remove_spaces(machine, dev_name); osd_printf_verbose("%s: Logical id %d: %s\n", label, dev + 1, devmap->map[dev].name); devmap->initialized = 1; } } } static device_info *devmap_class_register(running_machine &machine, device_map_t *devmap, int index, device_info **devlist, input_device_class devclass) { device_info *devinfo = NULL; char tempname[20]; if (*devmap->map[index].name == 0) { /* only map place holders if there were mappings specified is enabled */ if (devmap->initialized) { sprintf(tempname, "NC%d", index); devinfo = generic_device_alloc(devlist, tempname); devinfo->device = machine.input().device_class(devclass).add_device(devinfo->name.c_str(), devinfo); } return NULL; } else { devinfo = generic_device_alloc(devlist, devmap->map[index].name); devinfo->device = machine.input().device_class(devclass).add_device(devinfo->name.c_str(), devinfo); } return devinfo; } //============================================================ // sdlinput_register_joysticks //============================================================ static void sdlinput_register_joysticks(running_machine &machine) { device_info *devinfo; int physical_stick, axis, button, hat, stick, ball; char tempname[512]; SDL_Joystick *joy; devmap_init(machine, &joy_map, SDLOPTION_JOYINDEX, 8, "Joystick mapping"); osd_printf_verbose("Joystick: Start initialization\n"); for (physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++) { char *joy_name; #if (SDLMAME_SDL2) joy = SDL_JoystickOpen(physical_stick); joy_name = remove_spaces(machine, SDL_JoystickName(joy)); SDL_JoystickClose(joy); #else joy_name = remove_spaces(machine, SDL_JoystickName(physical_stick)); #endif devmap_register(&joy_map, physical_stick, joy_name); } for (stick = 0; stick < MAX_DEVMAP_ENTRIES; stick++) { devinfo = devmap_class_register(machine, &joy_map, stick, &joystick_list, DEVICE_CLASS_JOYSTICK); if (devinfo == NULL) continue; physical_stick = joy_map.map[stick].physical; joy = SDL_JoystickOpen(physical_stick); devinfo->joystick.device = joy; osd_printf_verbose("Joystick: %s\n", devinfo->name.c_str()); osd_printf_verbose("Joystick: ... %d axes, %d buttons %d hats %d balls\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy), SDL_JoystickNumBalls(joy)); osd_printf_verbose("Joystick: ... Physical id %d mapped to logical id %d\n", physical_stick, stick + 1); // loop over all axes for (axis = 0; axis < SDL_JoystickNumAxes(joy); axis++) { input_item_id itemid; if (axis < INPUT_MAX_AXIS) itemid = (input_item_id) (ITEM_ID_XAXIS + axis); else if (axis < INPUT_MAX_AXIS + INPUT_MAX_ADD_ABSOLUTE) itemid = (input_item_id) (ITEM_ID_ADD_ABSOLUTE1 - INPUT_MAX_AXIS + axis); else itemid = ITEM_ID_OTHER_AXIS_ABSOLUTE; sprintf(tempname, "A%d %s", axis, devinfo->name.c_str()); devinfo->device->add_item(tempname, itemid, generic_axis_get_state, &devinfo->joystick.axes[axis]); } // loop over all buttons for (button = 0; button < SDL_JoystickNumButtons(joy); button++) { input_item_id itemid; devinfo->joystick.buttons[button] = 0; if (button < INPUT_MAX_BUTTONS) itemid = (input_item_id) (ITEM_ID_BUTTON1 + button); else if (button < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH) itemid = (input_item_id) (ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + button); else itemid = ITEM_ID_OTHER_SWITCH; sprintf(tempname, "button %d", button); devinfo->device->add_item(tempname, itemid, generic_button_get_state, &devinfo->joystick.buttons[button]); } // loop over all hats for (hat = 0; hat < SDL_JoystickNumHats(joy); hat++) { input_item_id itemid; sprintf(tempname, "hat %d Up", hat); itemid = (input_item_id) ((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1UP + 4 * hat : ITEM_ID_OTHER_SWITCH); devinfo->device->add_item(tempname, itemid, generic_button_get_state, &devinfo->joystick.hatsU[hat]); sprintf(tempname, "hat %d Down", hat); itemid = (input_item_id) ((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1DOWN + 4 * hat : ITEM_ID_OTHER_SWITCH); devinfo->device->add_item(tempname, itemid, generic_button_get_state, &devinfo->joystick.hatsD[hat]); sprintf(tempname, "hat %d Left", hat); itemid = (input_item_id) ((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1LEFT + 4 * hat : ITEM_ID_OTHER_SWITCH); devinfo->device->add_item(tempname, itemid, generic_button_get_state, &devinfo->joystick.hatsL[hat]); sprintf(tempname, "hat %d Right", hat); itemid = (input_item_id) ((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1RIGHT + 4 * hat : ITEM_ID_OTHER_SWITCH); devinfo->device->add_item(tempname, itemid, generic_button_get_state, &devinfo->joystick.hatsR[hat]); } // loop over all (track)balls for (ball = 0; ball < SDL_JoystickNumBalls(joy); ball++) { int itemid; if (ball * 2 < INPUT_MAX_ADD_RELATIVE) itemid = ITEM_ID_ADD_RELATIVE1 + ball * 2; else itemid = ITEM_ID_OTHER_AXIS_RELATIVE; sprintf(tempname, "R%d %s", ball * 2, devinfo->name.c_str()); devinfo->device->add_item(tempname, (input_item_id) itemid, generic_axis_get_state, &devinfo->joystick.balls[ball * 2]); sprintf(tempname, "R%d %s", ball * 2 + 1, devinfo->name.c_str()); devinfo->device->add_item(tempname, (input_item_id) (itemid + 1), generic_axis_get_state, &devinfo->joystick.balls[ball * 2 + 1]); } } osd_printf_verbose("Joystick: End initialization\n"); } //============================================================ // sdlinput_deregister_joysticks //============================================================ static void sdlinput_deregister_joysticks(running_machine &machine) { device_info *curdev; osd_printf_verbose("Joystick: Start deinitialization\n"); for (curdev = joystick_list; curdev != NULL; curdev = curdev->next) { SDL_JoystickClose(curdev->joystick.device); } osd_printf_verbose("Joystick: End deinitialization\n"); } //============================================================ // sdlinput_register_mice //============================================================ #if defined(SDL2_MULTIAPI) && 0 static void sdlinput_register_mice(running_machine &machine) { int index, physical_mouse; mouse_enabled = machine.options().mouse(); devmap_init(machine, &mouse_map, SDLOPTION_MOUSEINDEX, 8, "Mouse mapping"); for (physical_mouse = 0; physical_mouse < SDL_GetNumMice(); physical_mouse++) { char *mouse_name = remove_spaces(machine, SDL_GetMouseName(physical_mouse)); devmap_register(&mouse_map, physical_mouse, mouse_name); } osd_printf_verbose("Mouse: Start initialization\n"); for (index = 0; index < MAX_DEVMAP_ENTRIES; index++) { device_info *devinfo; char defname[90]; int button; devinfo = devmap_class_register(machine, &mouse_map, index, &mouse_list, DEVICE_CLASS_MOUSE); if (devinfo == NULL) continue; // add the axes sprintf(defname, "X %s", devinfo->name.c_str()); devinfo->device->add_item(defname, ITEM_ID_XAXIS, generic_axis_get_state, &devinfo->mouse.lX); sprintf(defname, "Y %s", devinfo->name.c_str()); devinfo->device->add_item(defname, ITEM_ID_YAXIS, generic_axis_get_state, &devinfo->mouse.lY); for (button = 0; button < 4; button++) { input_item_id itemid; sprintf(defname, "B%d", button + 1); itemid = (input_item_id) (ITEM_ID_BUTTON1+button); devinfo->device->add_item(defname, itemid, generic_button_get_state, &devinfo->mouse.buttons[button]); } if (0 && mouse_enabled) SDL_SetRelativeMouseMode(index, SDL_TRUE); osd_printf_verbose("Mouse: Registered %s\n", devinfo->name.c_str()); } osd_printf_verbose("Mouse: End initialization\n"); } #else static void sdlinput_register_mice(running_machine &machine) { device_info *devinfo; char defname[20]; int button; osd_printf_verbose("Mouse: Start initialization\n"); mouse_map.logical[0] = 0; // SDL 1.2 has only 1 mouse - 1.3+ will also change that, so revisit this then devinfo = generic_device_alloc(&mouse_list, "System mouse"); devinfo->device = machine.input().device_class(DEVICE_CLASS_MOUSE).add_device(devinfo->name.c_str(), devinfo); mouse_enabled = machine.options().mouse(); // add the axes devinfo->device->add_item("X", ITEM_ID_XAXIS, generic_axis_get_state, &devinfo->mouse.lX); devinfo->device->add_item("Y", ITEM_ID_YAXIS, generic_axis_get_state, &devinfo->mouse.lY); for (button = 0; button < 4; button++) { input_item_id itemid = (input_item_id) (ITEM_ID_BUTTON1+button); sprintf(defname, "B%d", button + 1); devinfo->device->add_item(defname, itemid, generic_button_get_state, &devinfo->mouse.buttons[button]); } osd_printf_verbose("Mouse: Registered %s\n", devinfo->name.c_str()); osd_printf_verbose("Mouse: End initialization\n"); } #endif #if (USE_XINPUT) //============================================================ // lightgun helpers: copy-past from xinfo //============================================================ XDeviceInfo* find_device_info(Display *display, char *name, Bool only_extended) { XDeviceInfo *devices; XDeviceInfo *found = NULL; int loop; int num_devices; int len = strlen(name); Bool is_id = True; XID id = (XID)-1; for(loop=0; loop= IsXExtensionDevice)) && ((!is_id && strcmp(devices[loop].name, name) == 0) || (is_id && devices[loop].id == id))) { if (found) { fprintf(stderr, "Warning: There are multiple devices named \"%s\".\n" "To ensure the correct one is selected, please use " "the device ID instead.\n\n", name); } else { found = &devices[loop]; } } } return found; } //Copypasted from xinfo static int register_events(Display *dpy, XDeviceInfo *info, char *dev_name, Bool handle_proximity) { int number = 0; /* number of events registered */ XEventClass event_list[7]; int i; XDevice *device; Window root_win; unsigned long screen; XInputClassInfo *ip; screen = DefaultScreen(dpy); root_win = RootWindow(dpy, screen); device = XOpenDevice(dpy, info->id); if (!device) { fprintf(stderr, "unable to open device %s\n", dev_name); return 0; } if (device->num_classes > 0) { for (ip = device->classes, i=0; inum_classes; ip++, i++) { switch (ip->input_class) { case KeyClass: DeviceKeyPress(device, key_press_type, event_list[number]); number++; DeviceKeyRelease(device, key_release_type, event_list[number]); number++; break; case ButtonClass: DeviceButtonPress(device, button_press_type, event_list[number]); number++; DeviceButtonRelease(device, button_release_type, event_list[number]); number++; break; case ValuatorClass: DeviceMotionNotify(device, motion_type, event_list[number]); number++; fprintf(stderr, "Motion = %i\n",motion_type); if (handle_proximity) { ProximityIn(device, proximity_in_type, event_list[number]); number++; ProximityOut(device, proximity_out_type, event_list[number]); number++; } break; default: fprintf(stderr, "unknown class\n"); break; } } if (XSelectExtensionEvent(dpy, root_win, event_list, number)) { fprintf(stderr, "error selecting extended events\n"); return 0; } } return number; } //============================================================ // sdlinput_register_lightguns //============================================================ static void sdlinput_register_lightguns(running_machine &machine) { int index; XExtensionVersion *version; lightgun_enabled = machine.options().lightgun(); devmap_init(machine, &lightgun_map, SDLOPTION_LIGHTGUNINDEX, 8, "Lightgun mapping"); XDisplay = XOpenDisplay(NULL); if (XDisplay == NULL) { fprintf(stderr, "Unable to connect to X server\n"); return; } version = XGetExtensionVersion(XDisplay, INAME); if (!version || (version == (XExtensionVersion*) NoSuchExtension)) { fprintf(stderr, "xinput extension not available!\n"); return; } for (index=0; index<8; index++) { XDeviceInfo *info; if (strlen(lightgun_map.map[index].name)!=0) { device_info *devinfo; char *name=lightgun_map.map[index].name; char defname[512]; devinfo = devmap_class_register(machine, &lightgun_map, index, &lightgun_list, DEVICE_CLASS_LIGHTGUN); fprintf(stderr, "%i: %s\n",index, name); info=find_device_info(XDisplay, name, 0); if (!info) continue; //Grab device info and translate to stuff mame can use if (info->num_classes > 0) { XAnyClassPtr any = (XAnyClassPtr) (info->inputclassinfo); int i; for (i=0; inum_classes; i++) { int button; XValuatorInfoPtr v; XAxisInfoPtr a; int j; XButtonInfoPtr b; #if defined(__cplusplus) || defined(c_plusplus) switch (any->c_class) { #else switch (any->class) { #endif case ButtonClass: b = (XButtonInfoPtr) any; for (button = 0; button < b->num_buttons; button++) { input_item_id itemid; itemid = (input_item_id) (ITEM_ID_BUTTON1 + button); sprintf(defname, "B%d", button + 1); devinfo->device->add_item(defname, itemid, generic_button_get_state, &devinfo->lightgun.buttons[button]); } break; case ValuatorClass: v = (XValuatorInfoPtr) any; a = (XAxisInfoPtr) ((char *) v + sizeof (XValuatorInfo)); for (j=0; jnum_axes; j++, a++) { if (j==0) { #if (USE_XINPUT_DEBUG) fprintf(stderr, "For index %d: Set minx=%d, maxx=%d\n", index, a->min_value, a->max_value); #endif devinfo->lightgun.maxx=a->max_value; devinfo->lightgun.minx=a->min_value; } if (j==1) { #if (USE_XINPUT_DEBUG) fprintf(stderr, "For index %d: Set miny=%d, maxy=%d\n", index, a->min_value, a->max_value); #endif devinfo->lightgun.maxy=a->max_value; devinfo->lightgun.miny=a->min_value; } } break; } any = (XAnyClassPtr) ((char *) any + any->length); } } sprintf(defname, "X %s", devinfo->name.c_str()); devinfo->device->add_item(defname, ITEM_ID_XAXIS, generic_axis_get_state, &devinfo->lightgun.lX); sprintf(defname, "Y %s", devinfo->name.c_str()); devinfo->device->add_item(defname, ITEM_ID_YAXIS, generic_axis_get_state, &devinfo->lightgun.lY); devinfo->lightgun.deviceid=info->id; if (!info) { fprintf(stderr, "Can't find device %s!\n", lightgun_map.map[index].name); } else { fprintf(stderr, "Device %i: Registered %i events.\n",(int)info->id, register_events(XDisplay, info, lightgun_map.map[index].name, 0)); } } } osd_printf_verbose("Lightgun: End initialization\n"); } #endif //============================================================ // lookup_sdl_code //============================================================ static int lookup_sdl_code(const char *scode) { int i=0; while (sdl_lookup_table[i].code>=0) { if (!strcmp(scode, sdl_lookup_table[i].name)) return sdl_lookup_table[i].code; i++; } return -1; } //============================================================ // lookup_mame_code //============================================================ static int lookup_mame_index(const char *scode) { int index, i; index=-1; i=0; while (sdl_key_trans_table[i].mame_key != ITEM_ID_INVALID) { if (!strcmp(scode, sdl_key_trans_table[i].mame_key_name)) { index=i; break; } i++; } return index; } static input_item_id lookup_mame_code(const char *scode) { int index; index = lookup_mame_index(scode); if (index >= 0) return sdl_key_trans_table[index].mame_key; else return ITEM_ID_INVALID; } //============================================================ // sdlinput_read_keymap //============================================================ static kt_table * sdlinput_read_keymap(running_machine &machine) { char *keymap_filename; kt_table *key_trans_table; FILE *keymap_file; int line = 1; int index,i, sk, vk, ak; char buf[256]; char mks[41]; char sks[41]; char kns[41]; int sdl2section=0; if (!machine.options().bool_value(SDLOPTION_KEYMAP)) return sdl_key_trans_table; keymap_filename = (char *)downcast(machine.options()).keymap_file(); osd_printf_verbose("Keymap: Start reading keymap_file %s\n", keymap_filename); keymap_file = fopen(keymap_filename, "r"); if (keymap_file == NULL) { osd_printf_warning( "Keymap: Unable to open keymap %s, using default\n", keymap_filename); return sdl_key_trans_table; } key_trans_table = auto_alloc_array(machine, kt_table, ARRAY_LENGTH(sdl_key_trans_table)); memcpy((void *) key_trans_table, sdl_key_trans_table, sizeof(sdl_key_trans_table)); while (!feof(keymap_file)) { char *ret = fgets(buf, 255, keymap_file); if (ret && buf[0] != '\n' && buf[0] != '#') { buf[255]=0; i=strlen(buf); if (i && buf[i-1] == '\n') buf[i-1] = 0; if (strncmp(buf,"[SDL2]",6) == 0) { sdl2section = 1; } else if (((SDLMAME_SDL2) ^ sdl2section) == 0) { mks[0]=0; sks[0]=0; memset(kns, 0, ARRAY_LENGTH(kns)); sscanf(buf, "%40s %40s %x %x %40c\n", mks, sks, &vk, &ak, kns); index=lookup_mame_index(mks); sk = lookup_sdl_code(sks); if ( sk >= 0 && index >=0) { key_trans_table[index].sdl_key = sk; // vk and ak are not really needed //key_trans_table[index][VIRTUAL_KEY] = vk; //key_trans_table[index][ASCII_KEY] = ak; key_trans_table[index].ui_name = auto_alloc_array(machine, char, strlen(kns)+1); strcpy(key_trans_table[index].ui_name, kns); osd_printf_verbose("Keymap: Mapped <%s> to <%s> with ui-text <%s>\n", sks, mks, kns); } else osd_printf_warning("Keymap: Error on line %d - %s key not found: %s\n", line, (sk<0) ? "sdl" : "mame", buf); } } line++; } fclose(keymap_file); osd_printf_verbose("Keymap: Processed %d lines\n", line); return key_trans_table; } //============================================================ // sdlinput_register_keyboards //============================================================ #ifdef SDL2_MULTIAPI static void sdlinput_register_keyboards(running_machine &machine) { int physical_keyboard; int index; kt_table *key_trans_table; key_trans_table = sdlinput_read_keymap(machine); devmap_init(machine, &keyboard_map, SDLOPTION_KEYBINDEX, 8, "Keyboard mapping"); for (physical_keyboard = 0; physical_keyboard < SDL_GetNumKeyboards(); physical_keyboard++) { //char defname[90]; //snprintf(defname, sizeof(defname)-1, "Keyboard #%d", physical_keyboard + 1); char *defname = remove_spaces(machine, SDL_GetKeyboardName(SDL_GetKeyboard(physical_keyboard) )); devmap_register(&keyboard_map, physical_keyboard, defname); } osd_printf_verbose("Keyboard: Start initialization\n"); for (index = 0; index < MAX_DEVMAP_ENTRIES; index++) { device_info *devinfo; char defname[90]; int keynum; devinfo = devmap_class_register(machine, &keyboard_map, index, &keyboard_list, DEVICE_CLASS_KEYBOARD); if (devinfo == NULL) continue; // populate it for (keynum = 0; sdl_key_trans_table[keynum].mame_key!= ITEM_ID_INVALID; keynum++) { input_item_id itemid; itemid = key_trans_table[keynum].mame_key; // generate the default / modified name snprintf(defname, sizeof(defname)-1, "%s", key_trans_table[keynum].ui_name); // add the item to the device devinfo->device->add_item(defname, itemid, generic_button_get_state, &devinfo->keyboard.state[OSD_SDL_INDEX(key_trans_table[keynum].sdl_key)]); } osd_printf_verbose("Keyboard: Registered %s\n", devinfo->name.c_str()); } osd_printf_verbose("Keyboard: End initialization\n"); } #else static void sdlinput_register_keyboards(running_machine &machine) { device_info *devinfo; char defname[20]; int keynum; kt_table *key_trans_table; key_trans_table = sdlinput_read_keymap(machine); keyboard_map.logical[0] = 0; osd_printf_verbose("Keyboard: Start initialization\n"); // SDL 1.2 only has 1 keyboard (1.3+ will have multiple, this must be revisited then) // add it now devinfo = generic_device_alloc(&keyboard_list, "System keyboard"); devinfo->device = machine.input().device_class(DEVICE_CLASS_KEYBOARD).add_device(devinfo->name.c_str(), devinfo); // populate it for (keynum = 0; sdl_key_trans_table[keynum].mame_key != ITEM_ID_INVALID; keynum++) { input_item_id itemid; itemid = key_trans_table[keynum].mame_key; // generate the default / modified name snprintf(defname, sizeof(defname)-1, "%s", key_trans_table[keynum].ui_name); // add the item to the device // printf("Keynum %d => sdl key %d\n", keynum, OSD_SDL_INDEX(key_trans_table[keynum].sdl_key)); devinfo->device->add_item(defname, itemid, generic_button_get_state, &devinfo->keyboard.state[OSD_SDL_INDEX(key_trans_table[keynum].sdl_key)]); } osd_printf_verbose("Keyboard: Registered %s\n", devinfo->name.c_str()); osd_printf_verbose("Keyboard: End initialization\n"); } #endif //============================================================ // input_init //============================================================ bool sdl_osd_interface::input_init() { keyboard_list = NULL; joystick_list = NULL; mouse_list = NULL; lightgun_list = NULL; app_has_mouse_focus = 1; // register the keyboards sdlinput_register_keyboards(machine()); // register the mice sdlinput_register_mice(machine()); #if (USE_XINPUT) // register the lightguns sdlinput_register_lightguns(machine()); #endif if (machine().debug_flags & DEBUG_FLAG_OSD_ENABLED) { osd_printf_warning("Debug Build: Disabling input grab for -debug\n"); mouse_enabled = 0; } // get Sixaxis special mode info sixaxis_mode = options().sixaxis(); // register the joysticks sdlinput_register_joysticks(machine()); // now reset all devices device_list_reset_devices(keyboard_list); device_list_reset_devices(mouse_list); device_list_reset_devices(joystick_list); #if (USE_XINPUT) device_list_reset_devices(lightgun_list); #endif return true; } //============================================================ // sdlinput_pause //============================================================ void sdl_osd_interface::input_pause() { // keep track of the paused state input_paused = true; } void sdl_osd_interface::input_resume() { // keep track of the paused state input_paused = false; } //============================================================ // sdlinput_exit //============================================================ void sdl_osd_interface::input_exit() { // deregister sdlinput_deregister_joysticks(machine()); // free all devices device_list_free_devices(&keyboard_list); device_list_free_devices(&mouse_list); device_list_free_devices(&joystick_list); } //============================================================ // sdlinput_get_focus_window //============================================================ sdl_window_info *sdlinput_get_focus_window() { if (focus_window) // only be set on SDL >= 1.3 return focus_window; else return sdl_window_list; } #if (USE_XINPUT) device_info *get_lightgun_info_for_deviceid(XID deviceid) { device_info *devinfo; int index; //Find lightgun according to device id for (index=0; ; index++) { devinfo = generic_device_find_index(lightgun_list, index); if (devinfo==NULL) break; if (devinfo->lightgun.deviceid==deviceid) break; } return devinfo; } INT32 normalize_absolute_axis(INT32 raw, INT32 rawmin, INT32 rawmax) { INT32 rv; INT32 center = ((INT64)rawmax + (INT64)rawmin) / 2; // make sure we have valid data if (rawmin >= rawmax) { rv = raw; goto out; } // above center if (raw >= center) { INT32 result = (INT64)(raw - center) * (INT64)INPUT_ABSOLUTE_MAX / (INT64)(rawmax - center); rv = MIN(result, INPUT_ABSOLUTE_MAX); goto out; } // below center else { INT32 result = -((INT64)(center - raw) * (INT64)-INPUT_ABSOLUTE_MIN / (INT64)(center - rawmin)); rv = MAX(result, INPUT_ABSOLUTE_MIN); goto out; } out: #if (USE_XINPUT_DEBUG) fprintf(stderr, "raw: %d, rawmin: %d, rawmax: %d, center: %d, rv: %d, ABS_MIN: %d, ABS_MAX: %d\n", raw, rawmin, rawmax, center, rv, INPUT_ABSOLUTE_MIN, INPUT_ABSOLUTE_MAX); #endif return rv; } #endif //============================================================ // sdlinput_poll //============================================================ #if (SDLMAME_SDL2) static inline sdl_window_info * window_from_id(Uint32 windowID) { sdl_window_info *w; SDL_Window *window = SDL_GetWindowFromID(windowID); for (w = sdl_window_list; w != NULL; w = w->m_next) { //printf("w->window_id: %d\n", w->window_id); if (w->sdl_window() == window) { return w; } } return NULL; } static inline void resize_all_windows(void) { sdl_window_info *w; osd_ticks_t now = osd_ticks(); if (SDL13_COMBINE_RESIZE) { for (w = sdl_window_list; w != NULL; w = w->m_next) { if (w->m_resize_width && w->m_resize_height && ((now - w->m_last_resize) > osd_ticks_per_second() / 10)) { w->resize(w->m_resize_width, w->m_resize_height); w->m_resize_width = 0; w->m_resize_height = 0; } } } } #endif void sdlinput_process_events_buf() { SDL_Event event; if (SDLMAME_EVENTS_IN_WORKER_THREAD) { std::lock_guard lock(input_lock); #if (SDLMAME_SDL2) /* Make sure we get all pending events */ SDL_PumpEvents(); #endif while(SDL_PollEvent(&event)) { if (event_buf_count < MAX_BUF_EVENTS) event_buf[event_buf_count++] = event; else osd_printf_warning("Event Buffer Overflow!\n"); } } else SDL_PumpEvents(); } void sdlinput_poll(running_machine &machine) { device_info *devinfo; SDL_Event event; int index; // only for SDLMAME_EVENTS_IN_WORKER_THREAD SDL_Event loc_event_buf[MAX_BUF_EVENTS]; int loc_event_buf_count; int bufp; #if (USE_XINPUT) XEvent xevent; #endif for (index=0; ;index++) { devinfo = generic_device_find_index( mouse_list, index); if (devinfo == NULL) break; devinfo->mouse.lX = 0; devinfo->mouse.lY = 0; } #if (USE_XINPUT) //Get XInput events while (XPending(XDisplay)!=0) { XNextEvent(XDisplay, &xevent); if (xevent.type==motion_type) { XDeviceMotionEvent *motion = (XDeviceMotionEvent *) &xevent; #if (USE_XINPUT_DEBUG) /* * print a lot of debug informations of the motion event(s). */ fprintf(stderr, "XDeviceMotionEvent:\n" " type: %d\n" " serial: %lu\n" " send_event: %d\n" " display: %p\n" " window: --\n" " deviceid: %lu\n" " root: --\n" " subwindow: --\n" " time: --\n" " x: %d, y: %d\n" " x_root: %d, y_root: %d\n" " state: %u\n" " is_hint: %2.2X\n" " same_screen: %d\n" " device_state: %u\n" " axes_count: %2.2X\n" " first_axis: %2.2X\n" " axis_data[6]: {%d,%d,%d,%d,%d,%d}\n", motion->type, motion->serial, motion->send_event, motion->display, /* motion->window, */ motion->deviceid, /* motion->root */ /* motion->subwindow */ /* motion->time, */ motion->x, motion->y, motion->x_root, motion->y_root, motion->state, motion->is_hint, motion->same_screen, motion->device_state, motion->axes_count, motion->first_axis, motion->axis_data[0], motion->axis_data[1], motion->axis_data[2], motion->axis_data[3], motion->axis_data[4], motion->axis_data[5] ); #endif devinfo=get_lightgun_info_for_deviceid(motion->deviceid); /* * We have to check with axis will start on array index 0. * We have also to check the number of axes that are stored in the array. */ switch (motion->first_axis) { /* * Starting with x, check number of axis, if there is also the y axis stored. */ case 0: if (motion->axes_count >= 1) { devinfo->lightgun.lX=normalize_absolute_axis(motion->axis_data[0], devinfo->lightgun.minx, devinfo->lightgun.maxx); if (motion->axes_count >= 2) { devinfo->lightgun.lY=normalize_absolute_axis(motion->axis_data[1], devinfo->lightgun.miny, devinfo->lightgun.maxy); } } break; /* * Starting with y, ... */ case 1: if (motion->axes_count >= 1) { devinfo->lightgun.lY=normalize_absolute_axis(motion->axis_data[0], devinfo->lightgun.miny, devinfo->lightgun.maxy); } break; } } else if (xevent.type==button_press_type || xevent.type==button_release_type) { XDeviceButtonEvent *button = (XDeviceButtonEvent *) &xevent; devinfo=get_lightgun_info_for_deviceid(button->deviceid); devinfo->lightgun.buttons[button->button]=(xevent.type==button_press_type)?0x80:0; } } #endif if (SDLMAME_EVENTS_IN_WORKER_THREAD) { std::lock_guard lock(input_lock); memcpy(loc_event_buf, event_buf, sizeof(event_buf)); loc_event_buf_count = event_buf_count; event_buf_count = 0; bufp = 0; } while (TRUE) { if (SDLMAME_EVENTS_IN_WORKER_THREAD) { if (bufp >= loc_event_buf_count) break; event = loc_event_buf[bufp++]; } else { if (!SDL_PollEvent(&event)) break; } switch(event.type) { case SDL_KEYDOWN: #ifdef SDL2_MULTIAPI devinfo = generic_device_find_index( keyboard_list, keyboard_map.logical[event.key.which]); //printf("Key down %d %d %s => %d %s (scrlock keycode is %d)\n", event.key.which, event.key.keysym.scancode, devinfo->name.c_str(), OSD_SDL_INDEX_KEYSYM(&event.key.keysym), sdl_key_trans_table[event.key.keysym.scancode].mame_key_name, KEYCODE_SCRLOCK); #else devinfo = generic_device_find_index( keyboard_list, keyboard_map.logical[0]); #endif devinfo->keyboard.state[OSD_SDL_INDEX_KEYSYM(&event.key.keysym)] = 0x80; #if (SDLMAME_SDL2) if (event.key.keysym.sym < 0x20) machine.ui_input().push_char_event(sdl_window_list->target(), event.key.keysym.sym); #else ui_input_push_char_event(machine, sdl_window_list->target(), (unicode_char) event.key.keysym.unicode); #endif break; case SDL_KEYUP: #ifdef SDL2_MULTIAPI devinfo = generic_device_find_index( keyboard_list, keyboard_map.logical[event.key.which]); //printf("Key up: %d %d\n", OSD_SDL_INDEX_KEYSYM(&event.key.keysym), event.key.which); #else devinfo = generic_device_find_index( keyboard_list, keyboard_map.logical[0]); #endif devinfo->keyboard.state[OSD_SDL_INDEX_KEYSYM(&event.key.keysym)] = 0x00; break; case SDL_JOYAXISMOTION: devinfo = generic_device_find_index(joystick_list, joy_map.logical[event.jaxis.which]); if (devinfo) { if (sixaxis_mode) { int axis = event.jaxis.axis; if (axis <= 3) { devinfo->joystick.axes[event.jaxis.axis] = (event.jaxis.value * 2); } else { int magic = (event.jaxis.value / 2) + 16384; devinfo->joystick.axes[event.jaxis.axis] = magic; } } else { devinfo->joystick.axes[event.jaxis.axis] = (event.jaxis.value * 2); } } break; case SDL_JOYHATMOTION: devinfo = generic_device_find_index(joystick_list, joy_map.logical[event.jhat.which]); if (devinfo) { if (event.jhat.value & SDL_HAT_UP) { devinfo->joystick.hatsU[event.jhat.hat] = 0x80; } else { devinfo->joystick.hatsU[event.jhat.hat] = 0; } if (event.jhat.value & SDL_HAT_DOWN) { devinfo->joystick.hatsD[event.jhat.hat] = 0x80; } else { devinfo->joystick.hatsD[event.jhat.hat] = 0; } if (event.jhat.value & SDL_HAT_LEFT) { devinfo->joystick.hatsL[event.jhat.hat] = 0x80; } else { devinfo->joystick.hatsL[event.jhat.hat] = 0; } if (event.jhat.value & SDL_HAT_RIGHT) { devinfo->joystick.hatsR[event.jhat.hat] = 0x80; } else { devinfo->joystick.hatsR[event.jhat.hat] = 0; } } break; case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: devinfo = generic_device_find_index(joystick_list, joy_map.logical[event.jbutton.which]); if (devinfo) { devinfo->joystick.buttons[event.jbutton.button] = (event.jbutton.state == SDL_PRESSED) ? 0x80 : 0; } break; case SDL_MOUSEBUTTONDOWN: #ifdef SDL2_MULTIAPI devinfo = generic_device_find_index(mouse_list, mouse_map.logical[event.button.which]); #else devinfo = generic_device_find_index(mouse_list, mouse_map.logical[0]); #endif devinfo->mouse.buttons[event.button.button-1] = 0x80; //printf("But down %d %d %d %d %s\n", event.button.which, event.button.button, event.button.x, event.button.y, devinfo->name.c_str()); if (event.button.button == 1) { // FIXME Move static declaration static osd_ticks_t last_click = 0; static int last_x = 0; static int last_y = 0; int cx, cy; osd_ticks_t click = osd_ticks() * 1000 / osd_ticks_per_second(); sdl_window_info *window = GET_FOCUS_WINDOW(&event.button); if (window != NULL && window->xy_to_render_target(event.button.x,event.button.y, &cx, &cy) ) { machine.ui_input().push_mouse_down_event(window->target(), cx, cy); // FIXME Parameter ? if ((click-last_click < 250) && (cx >= last_x - 4 && cx <= last_x + 4) && (cy >= last_y - 4 && cy <= last_y + 4) ) { last_click = 0; machine.ui_input().push_mouse_double_click_event(window->target(), cx, cy); } else { last_click = click; last_x = cx; last_y = cy; } } } #if (!SDLMAME_SDL2) else if (event.button.button == 4) // SDL_BUTTON_WHEELUP { int cx, cy; sdl_window_info *window = GET_FOCUS_WINDOW(&event.button); if (window != NULL && window->xy_to_render_target(event.button.x,event.button.y, &cx, &cy) ) { machine.ui_input().push_mouse_wheel_event(window->target(), cx, cy, 120, 3); } } else if (event.button.button == 5) // SDL_BUTTON_WHEELDOWN { int cx, cy; sdl_window_info *window = GET_FOCUS_WINDOW(&event.button); if (window != NULL && window->xy_to_render_target(event.button.x,event.button.y, &cx, &cy) ) { machine.ui_input().push_mouse_wheel_event(window->target(), cx, cy, -120, 3); } } #endif break; #if (SDLMAME_SDL2) case SDL_MOUSEWHEEL: #ifdef SDL2_MULTIAPI devinfo = generic_device_find_index(mouse_list, mouse_map.logical[event.wheel.which]); #else devinfo = generic_device_find_index(mouse_list, mouse_map.logical[0]); #endif if (devinfo) { sdl_window_info *window = GET_FOCUS_WINDOW(&event.wheel); if (window != NULL) machine.ui_input().push_mouse_wheel_event(window->target(), 0, 0, event.wheel.y, 3); } break; #endif case SDL_MOUSEBUTTONUP: #ifdef SDL2_MULTIAPI devinfo = generic_device_find_index(mouse_list, mouse_map.logical[event.button.which]); #else devinfo = generic_device_find_index(mouse_list, mouse_map.logical[0]); #endif devinfo->mouse.buttons[event.button.button-1] = 0; //printf("But up %d %d %d %d\n", event.button.which, event.button.button, event.button.x, event.button.y); if (event.button.button == 1) { int cx, cy; sdl_window_info *window = GET_FOCUS_WINDOW(&event.button); if (window != NULL && window->xy_to_render_target(event.button.x,event.button.y, &cx, &cy) ) { machine.ui_input().push_mouse_up_event(window->target(), cx, cy); } } break; case SDL_MOUSEMOTION: #ifdef SDL2_MULTIAPI devinfo = generic_device_find_index(mouse_list, mouse_map.logical[event.motion.which]); #else devinfo = generic_device_find_index(mouse_list, mouse_map.logical[0]); #endif #if (SDLMAME_SDL2) // FIXME: may apply to 1.2 as well ... //printf("Motion %d %d %d %s\n", event.motion.which, event.motion.x, event.motion.y, devinfo->name.c_str()); devinfo->mouse.lX += event.motion.xrel * INPUT_RELATIVE_PER_PIXEL; devinfo->mouse.lY += event.motion.yrel * INPUT_RELATIVE_PER_PIXEL; #else devinfo->mouse.lX = event.motion.xrel * INPUT_RELATIVE_PER_PIXEL; devinfo->mouse.lY = event.motion.yrel * INPUT_RELATIVE_PER_PIXEL; #endif { int cx=-1, cy=-1; sdl_window_info *window =
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
// ImGui based debugger

#include "emu.h"
#include "debug_module.h"

#include "imgui/imgui.h"

#include "imagedev/floppy.h"

#include "debug/debugvw.h"
#include "debug/dvdisasm.h"
#include "debug/dvmemory.h"
#include "debug/dvbpoints.h"
#include "debug/dvwpoints.h"
#include "debug/debugcon.h"
#include "debug/debugcpu.h"
#include "debugger.h"
#include "render.h"
#include "ui/uimain.h"
#include "uiinput.h"

#include "formats/flopimg.h"

#include "config.h"
#include "modules/lib/osdobj_common.h"
#include "modules/osdmodule.h"
#include "zippath.h"

namespace osd {

namespace {

class debug_area
{
	DISABLE_COPYING(debug_area);

public:
	debug_area(running_machine &machine, debug_view_type type) :
		next(nullptr),
		type(0),
		ofs_x(0),
		ofs_y(0),
		is_collapsed(false),
		exec_cmd(false),
		scroll_end(false),
		scroll_follow(false)
	{
		this->view = machine.debug_view().alloc_view(type, nullptr, this);
		this->type = type;
		this->m_machine = &machine;
		this->width = 300;
		this->height = 300;
		this->console_prev.clear();

		/* specials */
		switch (type)
		{
		case DVT_DISASSEMBLY:
			/* set up disasm view */
			downcast<debug_view_disasm *>(this->view)->set_expression("curpc");
			break;
		default:
			break;
		}
	}
	~debug_area()
	{
		//this->target->debug_free(*this->container);
		machine().debug_view().free_view(*this->view);
	}

	running_machine &machine() const { assert(m_machine != nullptr); return *m_machine; }

	debug_area *        next;

	int                 type;
	debug_view *        view;
	running_machine *   m_machine;
	// drawing
	int                 ofs_x;
	int                 ofs_y;
	int                 width;
	int                 height;  // initial view size
	std::string         title;
	float               view_width;
	float               view_height;
	bool                has_focus;
	bool                is_collapsed;
	bool                exec_cmd;  // console only
	int                 src_sel;
	bool                scroll_end;
	bool                scroll_follow;  // set if view is to stay at the end of a scrollable area (like a log window)
	char                console_input[512];
	std::vector<std::string> console_history;
	std::string         console_prev;
};

class debug_imgui : public osd_module, public debug_module
{
public:
	debug_imgui() :
		osd_module(OSD_DEBUG_PROVIDER, "imgui"), debug_module(),
		m_machine(nullptr),
		m_take_ui(false),
		m_current_pointer(-1),
		m_mouse_x(0),
		m_mouse_y(0),
		m_mouse_button(false),
		m_prev_mouse_button(false),
		m_running(false),
		font_name(nullptr),
		font_size(0),
		m_key_char(0),
		m_hide(false),
		m_win_count(0),
		m_has_images(false),
		m_initialised(false),
		m_dialog_image(nullptr),
		m_filelist_refresh(false),
		m_mount_open(false),
		m_create_open(false),
		m_create_confirm_wait(false),
		m_selected_file(nullptr),
		m_format_sel(0)
	{
	}

	virtual ~debug_imgui() { }

	virtual int init(osd_interface &osd, const osd_options &options) override { return 0; }
	virtual void exit() override {};

	virtual void init_debugger(running_machine &machine) override;
	virtual void wait_for_debugger(device_t &device, bool firststop) override;
	virtual void debugger_update() override;

private:
	enum file_entry_type
	{
		DRIVE,
		DIRECTORY,
		FILE
	};

	struct file_entry
	{
		file_entry_type type;
		std::string basename;
		std::string fullpath;
	};

	struct image_type_entry
	{
		const floppy_image_format_t* format;
		std::string shortname;
		std::string longname;
	};

	void handle_events();
	void handle_mouse_views();
	void handle_keys_views();
	void handle_console(running_machine* machine);
	void update();
	void draw_images_menu();
	void draw_console();
	void add_disasm(int id);
	void add_memory(int id);
	void add_bpoints(int id);
	void add_wpoints(int id);
	void add_log(int id);
	void draw_disasm(debug_area* view_ptr, bool* opened);
	void draw_memory(debug_area* view_ptr, bool* opened);
	void draw_bpoints(debug_area* view_ptr, bool* opened);
	void draw_log(debug_area* view_ptr, bool* opened);
	void draw_view(debug_area* view_ptr, bool exp_change);
	void draw_mount_dialog(const char* label);
	void draw_create_dialog(const char* label);
	void mount_image();
	void create_image();
	void refresh_filelist();
	void refresh_typelist();
	void update_cpu_view(device_t* device);
	static bool get_view_source(void* data, int idx, const char** out_text);
	static int history_set(ImGuiInputTextCallbackData* data);

	running_machine* m_machine;
	bool             m_take_ui;
	int32_t          m_current_pointer;
	int32_t          m_mouse_x;
	int32_t          m_mouse_y;
	bool             m_mouse_button;
	bool             m_prev_mouse_button;
	bool             m_running;
	const char*      font_name;
	float            font_size;
	ImVec2           m_text_size;  // size of character (assumes monospaced font is in use)
	uint8_t          m_key_char;
	bool             m_hide;
	int              m_win_count;  // number of active windows, does not decrease, used to ID individual windows
	bool             m_has_images; // true if current system has any image devices
	bool             m_initialised;  // true after initial views are created
	device_image_interface* m_dialog_image;
	bool             m_filelist_refresh;  // set to true to refresh mount/create dialog file lists
	bool             m_mount_open;  // true when opening a mount dialog
	bool             m_create_open;  // true when opening a create dialog
	bool             m_create_confirm_wait;  // true if waiting for confirmation of the above
	std::vector<file_entry> m_filelist;
	std::vector<image_type_entry> m_typelist;
	file_entry*      m_selected_file;
	int              m_format_sel;
	char             m_path[1024];  // path text field buffer
	std::unordered_map<input_item_id,ImGuiKey> m_mapping;
};

// globals
static std::vector<debug_area*> view_list;
static debug_area* view_main_console = nullptr;
static debug_area* view_main_disasm = nullptr;
static debug_area* view_main_regs = nullptr;
static int history_pos;

static void view_list_add(debug_area* item)
{
	view_list.push_back(item);
}

static void view_list_remove(debug_area* item)
{
	std::vector<debug_area*>::iterator it;
	if(view_list.empty())
		return;
	it = std::find(view_list.begin(),view_list.end(),item);
	view_list.erase(it);

}

static debug_area *dview_alloc(running_machine &machine, debug_view_type type)
{
	return new debug_area(machine, type);
}

static inline void map_attr_to_fg_bg(unsigned char attr, rgb_t *fg, rgb_t *bg)
{
	*bg = rgb_t(0xe6,0xff,0xff,0xff);
	*fg = rgb_t(0xff,0x00,0x00,0x00);

	if(attr & DCA_ANCILLARY)
		*bg = rgb_t(0xcc,0xd0,0xd0,0xd0);
	if(attr & DCA_SELECTED) {
		*bg = rgb_t(0xcc,0xff,0x80,0x80);
	}
	if(attr & DCA_CURRENT) {
		*bg = rgb_t(0xcc,0xff,0xff,0x00);
	}
	if(attr & DCA_CHANGED) {
		*fg = rgb_t(0xff,0xff,0x00,0x00);
	}
	if(attr & DCA_INVALID) {
		*fg = rgb_t(0xff,0x00,0x00,0xff);
	}
	if(attr & DCA_DISABLED) {
		*fg = rgb_t(fg->a(), (fg->r() + bg->r()) >> 1, (fg->g() + bg->g()) >> 1, (fg->b() + bg->b()) >> 1);
	}
	if(attr & DCA_COMMENT) {
		*fg = rgb_t(0xff,0x00,0x80,0x00);
	}
}

bool debug_imgui::get_view_source(void* data, int idx, const char** out_text)
{
	auto* vw = static_cast<debug_view*>(data);
	*out_text = vw->source(idx)->name();
	return true;
}

void debug_imgui::handle_events()
{
	ImGuiIO& io = ImGui::GetIO();

	// find view that has focus (should only be one at a time)
	debug_area* focus_view = nullptr;
	for(auto view_ptr = view_list.begin();view_ptr != view_list.end(); ++view_ptr)
		if((*view_ptr)->has_focus)
			focus_view = *view_ptr;

	// check views in main views also (only the disassembler view accepts inputs)
	if(view_main_disasm)
		if(view_main_disasm->has_focus)
			focus_view = view_main_disasm;

	if(m_machine->input().code_pressed(KEYCODE_LCONTROL))
		io.KeyCtrl = true;
	else
		io.KeyCtrl = false;
	if(m_machine->input().code_pressed(KEYCODE_LSHIFT))
		io.KeyShift = true;
	else
		io.KeyShift = false;
	if(m_machine->input().code_pressed(KEYCODE_LALT))
		io.KeyAlt = true;
	else
		io.KeyAlt = false;

	for(input_item_id id = ITEM_ID_A; id <= ITEM_ID_CANCEL; ++id)
	{
		if(m_machine->input().code_pressed(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
		{
			if(m_mapping.count(id))
				io.AddKeyEvent(m_mapping[id], true);
		}
		else
		{
			if(m_mapping.count(id))
				io.AddKeyEvent(m_mapping[id], false);
		}
	}

	m_prev_mouse_button = m_mouse_button;
	m_key_char = 0;
	ui_event event;
	while(m_machine->ui_input().pop_event(&event))
	{
		switch (event.event_type)
		{
		case ui_event::type::POINTER_UPDATE:
			if(&m_machine->render().ui_target() != event.target)
				break;
			if(event.pointer_id != m_current_pointer)
			{
				if((0 > m_current_pointer) || ((event.pointer_pressed & 1) && !m_mouse_button))
					m_current_pointer = event.pointer_id;
			}
			if(event.pointer_id == m_current_pointer)
			{
				bool changed = (m_mouse_x != event.pointer_x) || (m_mouse_y != event.pointer_y) || (m_mouse_button != bool(event.pointer_buttons & 1));
				m_mouse_x = event.pointer_x;
				m_mouse_y = event.pointer_y;
				m_mouse_button = bool(event.pointer_buttons & 1);
				if(changed)
				{
					io.MousePos = ImVec2(m_mouse_x,m_mouse_y);
					io.MouseDown[0] = m_mouse_button;
				}
			}
			break;
		case ui_event::type::POINTER_LEAVE:
		case ui_event::type::POINTER_ABORT:
			if((&m_machine->render().ui_target() == event.target) && (event.pointer_id == m_current_pointer))
			{
				m_current_pointer = -1;
				bool changed = (m_mouse_x != event.pointer_x) || (m_mouse_y != event.pointer_y) || m_mouse_button;
				m_mouse_x = event.pointer_x;
				m_mouse_y = event.pointer_y;
				m_mouse_button = false;
				if(changed)
				{
					io.MousePos = ImVec2(m_mouse_x,m_mouse_y);
					io.MouseDown[0] = m_mouse_button;
				}
			}
			break;
		case ui_event::type::IME_CHAR:
			m_key_char = event.ch; // FIXME: assigning 4-byte UCS4 character to 8-bit variable
			if(focus_view)
				focus_view->view->process_char(m_key_char);
			return;
		default:
			break;
		}
	}

	// global keys
	if(ImGui::IsKeyPressed(ImGuiKey_F3,false))
	{
		if(ImGui::IsKeyDown(ImGuiKey_LeftShift))
			m_machine->schedule_hard_reset();
		else
		{
			m_machine->schedule_soft_reset();
			m_machine->debugger().console().get_visible_cpu()->debug()->go();
		}
	}

	if(ImGui::IsKeyPressed(ImGuiKey_F5,false))
	{
		m_machine->debugger().console().get_visible_cpu()->debug()->go();
		m_running = true;
	}
	if(ImGui::IsKeyPressed(ImGuiKey_F6,false))
	{
		m_machine->debugger().console().get_visible_cpu()->debug()->go_next_device();
		m_running = true;
	}
	if(ImGui::IsKeyPressed(ImGuiKey_F7,false))
	{
		m_machine->debugger().console().get_visible_cpu()->debug()->go_interrupt();
		m_running = true;
	}
	if(ImGui::IsKeyPressed(ImGuiKey_F8,false))
		m_machine->debugger().console().get_visible_cpu()->debug()->go_vblank();
	if(ImGui::IsKeyPressed(ImGuiKey_F9,false))
		m_machine->debugger().console().get_visible_cpu()->debug()->single_step_out();
	if(ImGui::IsKeyPressed(ImGuiKey_F10,false))
		m_machine->debugger().console().get_visible_cpu()->debug()->single_step_over();
	if(ImGui::IsKeyPressed(ImGuiKey_F11,false))
		m_machine->debugger().console().get_visible_cpu()->debug()->single_step();
	if(ImGui::IsKeyPressed(ImGuiKey_F12,false))
	{
		m_machine->debugger().console().get_visible_cpu()->debug()->go();
		m_hide = true;
	}

	if(ImGui::IsKeyPressed(ImGuiKey_D,false) && io.KeyCtrl)
		add_disasm(++m_win_count);
	if(ImGui::IsKeyPressed(ImGuiKey_M,false) && io.KeyCtrl)
		add_memory(++m_win_count);
	if(ImGui::IsKeyPressed(ImGuiKey_B,false) && io.KeyCtrl)
		add_bpoints(++m_win_count);
	if(ImGui::IsKeyPressed(ImGuiKey_W,false) && io.KeyCtrl)
		add_wpoints(++m_win_count);
	if(ImGui::IsKeyPressed(ImGuiKey_L,false) && io.KeyCtrl)
		add_log(++m_win_count);

}

void debug_imgui::handle_mouse_views()
{
	rectangle rect;
	bool clicked = false;
	if(m_mouse_button == true && m_prev_mouse_button == false)
		clicked = true;

	// check all views, and pass mouse clicks to them
	if(!m_mouse_button)
		return;
	rect.min_x = view_main_disasm->ofs_x;
	rect.min_y = view_main_disasm->ofs_y;
	rect.max_x = view_main_disasm->ofs_x + view_main_disasm->view_width;
	rect.max_y = view_main_disasm->ofs_y + view_main_disasm->view_height;
	if(rect.contains(m_mouse_x,m_mouse_y) && clicked && view_main_disasm->has_focus)
	{
		debug_view_xy topleft = view_main_disasm->view->visible_position();
		debug_view_xy newpos;
		newpos.x = topleft.x + (m_mouse_x-view_main_disasm->ofs_x) / m_text_size.x;
		newpos.y = topleft.y + (m_mouse_y-view_main_disasm->ofs_y) / m_text_size.y;
		view_main_disasm->view->set_cursor_position(newpos);
		view_main_disasm->view->set_cursor_visible(true);
	}
	for(auto it = view_list.begin();it != view_list.end();++it)
	{
		rect.min_x = (*it)->ofs_x;
		rect.min_y = (*it)->ofs_y;
		rect.max_x = (*it)->ofs_x + (*it)->view_width;
		rect.max_y = (*it)->ofs_y + (*it)->view_height;
		if(rect.contains(m_mouse_x,m_mouse_y) && clicked && (*it)->has_focus)
		{
			if((*it)->view->cursor_supported())
			{
				debug_view_xy topleft = (*it)->view->visible_position();
				debug_view_xy newpos;
				newpos.x = topleft.x + (m_mouse_x-(*it)->ofs_x) / m_text_size.x;
				newpos.y = topleft.y + (m_mouse_y-(*it)->ofs_y) / m_text_size.y;
				(*it)->view->set_cursor_position(newpos);
				(*it)->view->set_cursor_visible(true);
			}
		}
	}
}

void debug_imgui::handle_keys_views()
{
	debug_area* focus_view = nullptr;
	// find view that has focus (should only be one at a time)
	for(auto view_ptr = view_list.begin();view_ptr != view_list.end();++view_ptr)
		if((*view_ptr)->has_focus)
			focus_view = *view_ptr;

	// check views in main views also (only the disassembler view accepts inputs)
	if(view_main_disasm != nullptr)
		if(view_main_disasm->has_focus)
			focus_view = view_main_disasm;

	// if no view has focus, then there's nothing to do
	if(focus_view == nullptr)
		return;

	// pass keypresses to debug view with focus
	if(ImGui::IsKeyPressed(ImGuiKey_UpArrow))
		focus_view->view->process_char(DCH_UP);
	if(ImGui::IsKeyPressed(ImGuiKey_DownArrow))
		focus_view->view->process_char(DCH_DOWN);
	if(ImGui::IsKeyPressed(ImGuiKey_LeftArrow))
	{
		if(ImGui::IsKeyDown(ImGuiKey_LeftCtrl))
			focus_view->view->process_char(DCH_CTRLLEFT);
		else
			focus_view->view->process_char(DCH_LEFT);
	}
	if(ImGui::IsKeyPressed(ImGuiKey_RightArrow))
	{
		if(ImGui::IsKeyDown(ImGuiKey_LeftCtrl))
			focus_view->view->process_char(DCH_CTRLRIGHT);
		else
			focus_view->view->process_char(DCH_RIGHT);
	}
	if(ImGui::IsKeyPressed(ImGuiKey_PageUp))
		focus_view->view->process_char(DCH_PUP);
	if(ImGui::IsKeyPressed(ImGuiKey_PageDown))
		focus_view->view->process_char(DCH_PDOWN);
	if(ImGui::IsKeyPressed(ImGuiKey_Home))
	{
		if(ImGui::IsKeyDown(ImGuiKey_LeftCtrl))
			focus_view->view->process_char(DCH_CTRLHOME);
		else
			focus_view->view->process_char(DCH_HOME);
	}
	if(ImGui::IsKeyPressed(ImGuiKey_End))
	{
		if(ImGui::IsKeyDown(ImGuiKey_LeftCtrl))
			focus_view->view->process_char(DCH_CTRLEND);
		else
			focus_view->view->process_char(DCH_END);
	}

}

void debug_imgui::handle_console(running_machine* machine)
{
	if(view_main_console->exec_cmd && view_main_console->type == DVT_CONSOLE)
	{
		// if console input is empty, then do a single step
		if(strlen(view_main_console->console_input) == 0)
		{
			m_machine->debugger().console().get_visible_cpu()->debug()->single_step();
			view_main_console->exec_cmd = false;
			history_pos = view_main_console->console_history.size();
			return;
		}
		m_machine->debugger().console().execute_command(view_main_console->console_input, true);
		// check for commands that start execution (so that input fields can be disabled)
		if(strcmp(view_main_console->console_input,"g") == 0)
			m_running = true;
		if(strncmp(view_main_console->console_input,"g ",2) == 0)
			m_running = true;
		if(strcmp(view_main_console->console_input,"go") == 0)
			m_running = true;
		if(strncmp(view_main_console->console_input,"go ",3) == 0)
			m_running = true;
		if(strcmp(view_main_console->console_input,"gi") == 0)
			m_running = true;
		if(strncmp(view_main_console->console_input,"gi ",3) == 0)
			m_running = true;
		if(strcmp(view_main_console->console_input,"gint") == 0)
			m_running = true;
		if(strncmp(view_main_console->console_input,"gint ",5) == 0)
			m_running = true;
		if(strcmp(view_main_console->console_input,"gt") == 0)
			m_running = true;
		if(strncmp(view_main_console->console_input,"gt ",3) == 0)
			m_running = true;
		if(strcmp(view_main_console->console_input,"gtime") == 0)
			m_running = true;
		if(strncmp(view_main_console->console_input,"gtime ",6) == 0)
			m_running = true;
		if(strcmp(view_main_console->console_input,"n") == 0)
			m_running = true;
		if(strcmp(view_main_console->console_input,"next") == 0)
			m_running = true;
		// don't bother adding to history if the current command matches the previous one
		if(view_main_console->console_prev != view_main_console->console_input)
		{
			view_main_console->console_history.emplace_back(std::string(view_main_console->console_input));
			view_main_console->console_prev = view_main_console->console_input;
		}
		history_pos = view_main_console->console_history.size();
		strcpy(view_main_console->console_input,"");
		view_main_console->exec_cmd = false;
	}
}

int debug_imgui::history_set(ImGuiInputTextCallbackData* data)
{
	if(view_main_console->console_history.size() == 0)
		return 0;

	switch(data->EventKey)
	{
		case ImGuiKey_UpArrow:
			if(history_pos > 0)
				history_pos--;
			break;
		case ImGuiKey_DownArrow:
			if(history_pos < view_main_console->console_history.size())
				history_pos++;
			break;
		default:
			break;
	}

	if(history_pos == view_main_console->console_history.size())
		data->CursorPos = data->BufTextLen = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", "");
	else
		data->CursorPos = data->BufTextLen = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", view_main_console->console_history[history_pos].c_str());

	data->BufDirty = true;
	return 0;
}

void debug_imgui::update_cpu_view(device_t* device)
{
	const debug_view_source *source;
	source = view_main_disasm->view->source_for_device(device);
	view_main_disasm->view->set_source(*source);
	source = view_main_regs->view->source_for_device(device);
	view_main_regs->view->set_source(*source);
}

void debug_imgui::draw_view(debug_area* view_ptr, bool exp_change)
{
	const debug_view_char *viewdata;
	ImDrawList* drawlist;
	debug_view_xy vsize,totalsize,pos;
	unsigned char v;
	int x,y;
	ImVec2 xy1,xy2;
	ImVec2 fsize = ImGui::CalcTextSize("A"); // any character will do, we should be using a monospaced font
	rgb_t bg, fg;
	rgb_t base(0xe6, 0xff, 0xff, 0xff);

	totalsize = view_ptr->view->total_size();

	ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0));
	ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0));

	// if the view has changed its expression (disasm, memory), then update scroll bar and view cursor
	if(exp_change)
	{
		if(view_ptr->view->cursor_supported())
		{
			view_ptr->view->set_cursor_visible(true);
			view_ptr->view->set_cursor_position(debug_view_xy(0,view_ptr->view->visible_position().y));
		}
		if(view_ptr->type != DVT_MEMORY)  // no scroll bars in memory views
			ImGui::SetScrollY(view_ptr->view->visible_position().y * fsize.y);
	}

	// update view location, while the cursor is at 0,0.
	view_ptr->ofs_x = ImGui::GetCursorScreenPos().x;
	view_ptr->ofs_y = ImGui::GetCursorScreenPos().y;
	view_ptr->view_width = ImGui::GetContentRegionAvail().x;
	view_ptr->view_height = ImGui::GetContentRegionAvail().y;
	view_ptr->has_focus = ImGui::IsWindowFocused();
	drawlist = ImGui::GetWindowDrawList();

	// temporarily set cursor to the last line, this will set the scroll bar range
	if(view_ptr->type != DVT_MEMORY)  // no scroll bars in memory views
	{
		ImGui::SetCursorPosY((totalsize.y) * fsize.y);
		ImGui::Dummy(ImVec2(0,0)); // some object is required for validation
	}

	// set the visible area to be displayed
	vsize.x = view_ptr->view_width / fsize.x;
	vsize.y = (view_ptr->view_height / fsize.y) + 1;
	view_ptr->view->set_visible_size(vsize);

	// set the visible position
	if(view_ptr->type != DVT_MEMORY)  // since ImGui cannot handle huge memory views, we'll just let the view control the displayed area
	{
		pos.x = 0;
		pos.y = ImGui::GetScrollY() / fsize.y;
		view_ptr->view->set_visible_position(pos);
	}

	viewdata = view_ptr->view->viewdata();

	xy1.x = view_ptr->ofs_x;
	xy1.y = view_ptr->ofs_y + ImGui::GetScrollY();
	xy2 = fsize;
	xy2.x += view_ptr->ofs_x;
	xy2.y += view_ptr->ofs_y + ImGui::GetScrollY();
	for(y=0;y<vsize.y;y++)
	{
		for(x=0;x<vsize.x;x++)
		{
			char str[2];
			map_attr_to_fg_bg(viewdata->attrib,&fg,&bg);
			ImU32 fg_col = IM_COL32(fg.r(),fg.g(),fg.b(),fg.a());
			str[0] = v = viewdata->byte;
			str[1] = '\0';
			if(bg != base)
			{
				ImU32 bg_col = IM_COL32(bg.r(),bg.g(),bg.b(),bg.a());
				xy1.x++; xy2.x++;
				drawlist->AddRectFilled(xy1,xy2,bg_col);
				xy1.x--; xy2.x--;
			}
			drawlist->AddText(xy1,fg_col,str);
			xy1.x += fsize.x;
			xy2.x += fsize.x;
			viewdata++;
		}
		xy1.x = view_ptr->ofs_x;
		xy2.x = view_ptr->ofs_x + fsize.x;
		xy1.y += fsize.y;
		xy2.y += fsize.y;
	}

	// draw a rect around a view if it has focus
	if(view_ptr->has_focus)
	{
		ImU32 col = IM_COL32(127,127,127,76);
		drawlist->AddRect(ImVec2(view_ptr->ofs_x,view_ptr->ofs_y + ImGui::GetScrollY()),
			ImVec2(view_ptr->ofs_x + view_ptr->view_width,view_ptr->ofs_y + ImGui::GetScrollY() + view_ptr->view_height),col);
	}

	// if the vertical scroll bar is at the end, then force it to the maximum value in case of an update
	if(view_ptr->scroll_end)
		ImGui::SetScrollY(ImGui::GetScrollMaxY());
	// and update the scroll end flag
	view_ptr->scroll_end = false;
	if(view_ptr->scroll_follow)
		if(ImGui::GetScrollY() == ImGui::GetScrollMaxY() || ImGui::GetScrollMaxY() < 0)
			view_ptr->scroll_end = true;

	ImGui::PopStyleVar(2);
}

void debug_imgui::draw_bpoints(debug_area* view_ptr, bool* opened)
{
	ImGui::SetNextWindowSize(ImVec2(view_ptr->width,view_ptr->height + ImGui::GetTextLineHeight()),ImGuiCond_Once);
	if(ImGui::Begin(view_ptr->title.c_str(),opened))
	{
		view_ptr->is_collapsed = false;
		ImGui::BeginChild("##break_output", ImVec2(ImGui::GetWindowWidth() - 16,ImGui::GetWindowHeight() - ImGui::GetTextLineHeight() - ImGui::GetCursorPosY()));  // account for title bar and widgets already drawn
		draw_view(view_ptr,false);
		ImGui::EndChild();

		ImGui::End();
	}
	else
		view_ptr->is_collapsed = true;
}

void debug_imgui::add_bpoints(int id)
{
	std::stringstream str;
	debug_area* new_view;
	new_view = dview_alloc(*m_machine, DVT_BREAK_POINTS);
	str << id;
	str << ": Breakpoints";
	new_view->title = str.str();
	new_view->width = 500;
	new_view->height = 300;
	new_view->ofs_x = 0;
	new_view->ofs_y = 0;
	view_list_add(new_view);
}

void debug_imgui::add_wpoints(int id)
{
	std::stringstream str;
	debug_area* new_view;
	new_view = dview_alloc(*m_machine, DVT_WATCH_POINTS);
	str << id;
	str << ": Watchpoints";
	new_view->title = str.str();
	new_view->width = 500;
	new_view->height = 300;
	new_view->ofs_x = 0;
	new_view->ofs_y = 0;
	view_list_add(new_view);
}

void debug_imgui::draw_log(debug_area* view_ptr, bool* opened)
{
	ImGui::SetNextWindowSize(ImVec2(view_ptr->width,view_ptr->height + ImGui::GetTextLineHeight()),ImGuiCond_Once);
	if(ImGui::Begin(view_ptr->title.c_str(),opened))
	{
		view_ptr->is_collapsed = false;
		ImGui::BeginChild("##log_output", ImVec2(ImGui::GetWindowWidth() - 16,ImGui::GetWindowHeight() - ImGui::GetTextLineHeight() - ImGui::GetCursorPosY()));  // account for title bar and widgets already drawn
		draw_view(view_ptr,false);
		ImGui::EndChild();

		ImGui::End();
	}
	else
		view_ptr->is_collapsed = true;
}

void debug_imgui::add_log(int id)
{
	std::stringstream str;
	debug_area* new_view;
	new_view = dview_alloc(*m_machine, DVT_LOG);
	str << id;
	str << ": Error log";
	new_view->title = str.str();
	new_view->width = 500;
	new_view->height = 300;
	new_view->ofs_x = 0;
	new_view->ofs_y = 0;
	new_view->scroll_follow = true;
	view_list_add(new_view);
}

void debug_imgui::draw_disasm(debug_area* view_ptr, bool* opened)
{
	ImGui::SetNextWindowSize(ImVec2(view_ptr->width,view_ptr->height + ImGui::GetTextLineHeight()),ImGuiCond_Once);
	if(ImGui::Begin(view_ptr->title.c_str(),opened,ImGuiWindowFlags_MenuBar))
	{
		bool exp_change = false;

		view_ptr->is_collapsed = false;
		if(ImGui::BeginMenuBar())
		{
			if(ImGui::BeginMenu("Options"))
			{
				auto* disasm = downcast<debug_view_disasm*>(view_ptr->view);
				int rightcol = disasm->right_column();

				if(ImGui::MenuItem("Raw opcodes", nullptr,(rightcol == DASM_RIGHTCOL_RAW) ? true : false))
					disasm->set_right_column(DASM_RIGHTCOL_RAW);
				if(ImGui::MenuItem("Encrypted opcodes", nullptr,(rightcol == DASM_RIGHTCOL_ENCRYPTED) ? true : false))
					disasm->set_right_column(DASM_RIGHTCOL_ENCRYPTED);
				if(ImGui::MenuItem("No opcodes", nullptr,(rightcol == DASM_RIGHTCOL_NONE) ? true : false))
					disasm->set_right_column(DASM_RIGHTCOL_NONE);
				if(ImGui::MenuItem("Comments", nullptr,(rightcol == DASM_RIGHTCOL_COMMENTS) ? true : false))
					disasm->set_right_column(DASM_RIGHTCOL_COMMENTS);

				ImGui::EndMenu();
			}
			ImGui::EndMenuBar();
		}

		ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll;
		if(m_running)
			flags |= ImGuiInputTextFlags_ReadOnly;
		ImGui::Combo("##cpu",&view_ptr->src_sel,get_view_source,view_ptr->view,view_ptr->view->source_count());
		ImGui::SameLine();
		ImGui::PushItemWidth(-1.0f);
		if(ImGui::InputText("##addr",view_ptr->console_input,512,flags))
		{
			downcast<debug_view_disasm *>(view_ptr->view)->set_expression(view_ptr->console_input);
			exp_change = true;
		}
		ImGui::PopItemWidth();
		ImGui::Separator();

		// disassembly portion
		unsigned idx = 0;
		const debug_view_source* src = view_ptr->view->source(idx);
		do
		{
			if(view_ptr->src_sel == idx)
				view_ptr->view->set_source(*src);
			src = view_ptr->view->source(++idx);
		}
		while (src);

		ImGui::BeginChild("##disasm_output", ImVec2(ImGui::GetWindowWidth() - 16,ImGui::GetWindowHeight() - ImGui::GetTextLineHeight() - ImGui::GetCursorPosY()));  // account for title bar and widgets already drawn
		draw_view(view_ptr,exp_change);
		ImGui::EndChild();

		ImGui::End();
	}
	else
		view_ptr->is_collapsed = true;
}

void debug_imgui::add_disasm(int id)
{
	std::stringstream str;
	debug_area* new_view;
	new_view = dview_alloc(*m_machine, DVT_DISASSEMBLY);
	str << id;
	str << ": Disassembly";
	new_view->title = str.str();
	new_view->width = 500;
	new_view->height = 300;
	new_view->ofs_x = 0;
	new_view->ofs_y = 0;
	new_view->src_sel = 0;
	strcpy(new_view->console_input,"curpc");
	view_list_add(new_view);
}

void debug_imgui::draw_memory(debug_area* view_ptr, bool* opened)
{
	ImGui::SetNextWindowSize(ImVec2(view_ptr->width,view_ptr->height + ImGui::GetTextLineHeight()),ImGuiCond_Once);
	if(ImGui::Begin(view_ptr->title.c_str(),opened,ImGuiWindowFlags_MenuBar))
	{
		bool exp_change = false;

		view_ptr->is_collapsed = false;
		if(ImGui::BeginMenuBar())
		{
			if(ImGui::BeginMenu("Options"))
			{
				auto* mem = downcast<debug_view_memory*>(view_ptr->view);
				bool physical = mem->physical();
				bool rev = mem->reverse();
				debug_view_memory::data_format format = mem->get_data_format();
				uint32_t chunks = mem->chunks_per_row();
				int radix = mem->address_radix();

				if(ImGui::MenuItem("1-byte hexadecimal", nullptr,(format == debug_view_memory::data_format::HEX_8BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::HEX_8BIT);
				if(ImGui::MenuItem("2-byte hexadecimal", nullptr,(format == debug_view_memory::data_format::HEX_16BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::HEX_16BIT);
				if(ImGui::MenuItem("4-byte hexadecimal", nullptr,(format == debug_view_memory::data_format::HEX_32BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::HEX_32BIT);
				if(ImGui::MenuItem("8-byte hexadecimal", nullptr,(format == debug_view_memory::data_format::HEX_64BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::HEX_64BIT);
				if(ImGui::MenuItem("1-byte octal", nullptr,(format == debug_view_memory::data_format::OCTAL_8BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::OCTAL_8BIT);
				if(ImGui::MenuItem("2-byte octal", nullptr,(format == debug_view_memory::data_format::OCTAL_16BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::OCTAL_16BIT);
				if(ImGui::MenuItem("4-byte octal", nullptr,(format == debug_view_memory::data_format::OCTAL_32BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::OCTAL_32BIT);
				if(ImGui::MenuItem("8-byte octal", nullptr,(format == debug_view_memory::data_format::OCTAL_64BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::OCTAL_64BIT);
				if(ImGui::MenuItem("32-bit floating point", nullptr,(format == debug_view_memory::data_format::FLOAT_32BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::FLOAT_32BIT);
				if(ImGui::MenuItem("64-bit floating point", nullptr,(format == debug_view_memory::data_format::FLOAT_64BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::FLOAT_64BIT);
				if(ImGui::MenuItem("80-bit floating point", nullptr,(format == debug_view_memory::data_format::FLOAT_80BIT) ? true : false))
					mem->set_data_format(debug_view_memory::data_format::FLOAT_80BIT);
				ImGui::Separator();
				if(ImGui::MenuItem("Hexadecimal Addresses", nullptr,(radix == 16)))
					mem->set_address_radix(16);
				if(ImGui::MenuItem("Decimal Addresses", nullptr,(radix == 10)))
					mem->set_address_radix(10);
				if(ImGui::MenuItem("Octal Addresses", nullptr,(radix == 8)))
					mem->set_address_radix(8);
				ImGui::Separator();
				if(ImGui::MenuItem("Logical addresses", nullptr,!physical))
					mem->set_physical(false);
				if(ImGui::MenuItem("Physical addresses", nullptr,physical))
					mem->set_physical(true);
				ImGui::Separator();
				if(ImGui::MenuItem("Reverse view", nullptr,rev))
					mem->set_reverse(!rev);
				ImGui::Separator();
				if(ImGui::MenuItem("Increase bytes per line"))
					mem->set_chunks_per_row(chunks+1);
				if(ImGui::MenuItem("Decrease bytes per line"))
					mem->set_chunks_per_row(chunks-1);

				ImGui::EndMenu();
			}
			ImGui::EndMenuBar();
		}

		ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll;
		ImGui::PushItemWidth(100.0f);
		if(m_running)
			flags |= ImGuiInputTextFlags_ReadOnly;
		if(ImGui::InputText("##addr",view_ptr->console_input,512,flags))
		{
			downcast<debug_view_memory *>(view_ptr->view)->set_expression(view_ptr->console_input);
			exp_change = true;
		}
		ImGui::PopItemWidth();
		ImGui::SameLine();
		ImGui::PushItemWidth(-1.0f);
		ImGui::Combo("##region",&view_ptr->src_sel,get_view_source,view_ptr->view,view_ptr->view->source_count());
		ImGui::PopItemWidth();
		ImGui::Separator();

		// memory editor portion
		unsigned idx = 0;
		const debug_view_source* src = view_ptr->view->source(idx);
		do
		{
			if(view_ptr->src_sel == idx)
				view_ptr->view->set_source(*src);
			src = view_ptr->view->source(++idx);
		}
		while (src);

		ImGui::BeginChild("##memory_output", ImVec2(ImGui::GetWindowWidth() - 16,ImGui::GetWindowHeight() - ImGui::GetTextLineHeight() - ImGui::GetCursorPosY()));  // account for title bar and widgets already drawn
		draw_view(view_ptr,exp_change);
		ImGui::EndChild();

		ImGui::End();
	}
	else
		view_ptr->is_collapsed = true;
}

void debug_imgui::add_memory(int id)
{
	std::stringstream str;
	debug_area* new_view;
	new_view = dview_alloc(*m_machine, DVT_MEMORY);
	str << id;
	str << ": Memory";
	new_view->title = str.str();
	new_view->width = 500;
	new_view->height = 300;
	new_view->ofs_x = 0;
	new_view->ofs_y = 0;
	new_view->src_sel = 0;
	strcpy(new_view->console_input,"0");
	view_list_add(new_view);
}

void debug_imgui::mount_image()
{
	if(m_selected_file != nullptr)
	{
		std::error_condition err;
		switch(m_selected_file->type)
		{
			case file_entry_type::DRIVE:
			case file_entry_type::DIRECTORY:
				{
					util::zippath_directory::ptr dir;
					err = util::zippath_directory::open(m_selected_file->fullpath, dir);
				}
				if(!err)
				{
					m_filelist_refresh = true;
					strcpy(m_path,m_selected_file->fullpath.c_str());
				}
				break;
			case file_entry_type::FILE:
				m_dialog_image->load(m_selected_file->fullpath);
				ImGui::CloseCurrentPopup();
				m_mount_open = false;
				break;
		}
	}
}

void debug_imgui::create_image()
{
	std::pair<std::error_condition, std::string> res;

	auto *fd = dynamic_cast<floppy_image_device *>(m_dialog_image);
	if(fd != nullptr)
	{
		res = fd->create(m_path,nullptr,nullptr);
		if(!res.first)
			fd->setup_write(m_typelist.at(m_format_sel).format);
	}
	else
		res = m_dialog_image->create(m_path,nullptr,nullptr);
	if(!res.first)
		ImGui::CloseCurrentPopup();
	// TODO: add a messagebox to display on an error
}

void debug_imgui::refresh_filelist()
{
	uint8_t first = 0;

	// todo
	m_filelist.clear();
	m_filelist_refresh = false;

	util::zippath_directory::ptr dir;
	std::error_condition const err = util::zippath_directory::open(m_path,dir);
	if(!err)
	{
		// add drives
		for(std::string const &volume_name : osd_get_volume_names())
		{
			file_entry temp;
			temp.type = file_entry_type::DRIVE;
			temp.basename = volume_name;
			temp.fullpath = volume_name;
			m_filelist.emplace_back(std::move(temp));
		}
		first = m_filelist.size();
		const directory::entry *dirent;
		while((dirent = dir->readdir()) != nullptr)
		{
			file_entry temp;
			switch(dirent->type)
			{
				case directory::entry::entry_type::FILE:
					temp.type = file_entry_type::FILE;
					break;
				case directory::entry::entry_type::DIR:
					temp.type = file_entry_type::DIRECTORY;
					break;
				default:
					break;
			}
			temp.basename = std::string(dirent->name);
			temp.fullpath = util::zippath_combine(m_path,dirent->name);
			m_filelist.emplace_back(std::move(temp));
		}
	}
	dir.reset();

	// sort file list, as it is not guaranteed to be in any particular order
	std::sort(m_filelist.begin()+first,m_filelist.end(),[](file_entry x, file_entry y) { return x.basename < y.basename; } );
}

void debug_imgui::refresh_typelist()
{
	auto *fd = static_cast<floppy_image_device *>(m_dialog_image);

	m_typelist.clear();
	if(m_dialog_image->formatlist().empty())
		return;
	if(fd == nullptr)
		return;

	for(const floppy_image_format_t* flist : fd->get_formats())
	{
		if(flist->supports_save())
		{
			image_type_entry temp;
			temp.format = flist;
			temp.shortname = flist->name();
			temp.longname = flist->description();
			m_typelist.emplace_back(std::move(temp));
		}
	}
}

void debug_imgui::draw_images_menu()
{
	if(ImGui::BeginMenu("Images"))
	{
		int x = 0;
		for (device_image_interface &img : image_interface_enumerator(m_machine->root_device()))
		{
			x++;
			std::string str = string_format(" %s : %s##%i",img.device().name(),img.exists() ? img.filename() : "[Empty slot]",x);
			if(ImGui::BeginMenu(str.c_str()))
			{
				if(ImGui::MenuItem("Mount..."))
				{
					m_dialog_image = &img;
					m_filelist_refresh = true;
					m_mount_open = true;
					m_selected_file = nullptr;  // start with no file selected
					if (img.exists())  // use image path if one is already mounted
						strcpy(m_path,util::zippath_parent(m_dialog_image->filename()).c_str());
					else
						strcpy(m_path,img.working_directory().c_str());
				}
				if(ImGui::MenuItem("Unmount"))
					img.unload();
				ImGui::Separator();
				if(img.is_creatable())
				{
					if(ImGui::MenuItem("Create..."))
					{
						m_dialog_image = &img;
						m_create_open = true;
						m_create_confirm_wait = false;
						refresh_typelist();
						strcpy(m_path,img.working_directory().c_str());
					}
				}
				// TODO: Cassette controls
				ImGui::EndMenu();
			}
		}
		ImGui::EndMenu();
	}
}

void debug_imgui::draw_mount_dialog(const char* label)
{
	// render dialog
	//ImGui::SetNextWindowContentWidth(200.0f);
	if(ImGui::BeginPopupModal(label,nullptr,ImGuiWindowFlags_AlwaysAutoResize))
	{
		if(m_filelist_refresh)
			refresh_filelist();
		if(ImGui::InputText("##mountpath",m_path,1024,ImGuiInputTextFlags_EnterReturnsTrue))
			m_filelist_refresh = true;
		ImGui::Separator();

		ImVec2 listbox_size;
		listbox_size.x = 0.0f;
		listbox_size.y = ImGui::GetTextLineHeightWithSpacing() * 15.25f;

		if(ImGui::BeginListBox("##filelist",listbox_size))
		{
			for(auto f = m_filelist.begin();f != m_filelist.end();++f)
			{
				std::string txt_name;
				bool sel = false;
				switch((*f).type)
				{
					case file_entry_type::DRIVE:
						txt_name.assign("[DRIVE] ");
						break;
					case file_entry_type::DIRECTORY:
						txt_name.assign("[DIR]   ");
						break;
					case file_entry_type::FILE:
						txt_name.assign("[FILE]  ");
						break;
				}
				txt_name.append((*f).basename);
				if(m_selected_file == &(*f))
					sel = true;
				if(ImGui::Selectable(txt_name.c_str(),sel,ImGuiSelectableFlags_AllowDoubleClick))
				{
					m_selected_file = &(*f);
					if(ImGui::IsMouseDoubleClicked(0))
					{
						mount_image();
					}
				}
			}
			ImGui::EndListBox();
		}
		ImGui::Separator();
		if(ImGui::Button("Cancel##mount"))
		{
			ImGui::CloseCurrentPopup();
			m_mount_open = false;
		}
		ImGui::SameLine();
		if(ImGui::Button("OK##mount"))
			mount_image();
		ImGui::EndPopup();
	}
}

void debug_imgui::draw_create_dialog(const char* label)
{
	// render dialog
	//ImGui::SetNextWindowContentWidth(200.0f);
	if(ImGui::BeginPopupModal(label,nullptr,ImGuiWindowFlags_AlwaysAutoResize))
	{
		ImGui::LabelText("##static1","Filename:");
		ImGui::SameLine();
		if(ImGui::InputText("##createfilename",m_path,1024,ImGuiInputTextFlags_EnterReturnsTrue))
		{
			auto entry = osd_stat(m_path);
			auto file_type = (entry != nullptr) ? entry->type : directory::entry::entry_type::NONE;
			if(file_type == directory::entry::entry_type::NONE)
				create_image();
			if(file_type == directory::entry::entry_type::FILE)
				m_create_confirm_wait = true;
			// cannot overwrite a directory, so nothing will be none in that case.
		}

		// format combo box for floppy devices
		auto *fd = dynamic_cast<floppy_image_device *>(m_dialog_image);
		if(fd != nullptr)
		{
			std::string combo_str;
			combo_str.clear();
			for(auto f = m_typelist.begin();f != m_typelist.end();++f)
			{
				// TODO: perhaps do this at the time the format list is generated, rather than every frame
				combo_str.append((*f).longname);
				combo_str.append(1,'\0');
			}
			combo_str.append(1,'\0');
			ImGui::Separator();
			ImGui::LabelText("##static2","Format:");
			ImGui::SameLine();
			ImGui::Combo("##formatcombo",&m_format_sel,combo_str.c_str(),m_typelist.size());
		}

		if(m_create_confirm_wait)
		{
			ImGui::Separator();
			ImGui::Text("File already exists.  Are you sure you wish to overwrite it?");
			ImGui::Separator();
			if(ImGui::Button("Cancel##mount"))
				ImGui::CloseCurrentPopup();
			ImGui::SameLine();
			if(ImGui::Button("OK##mount"))
				create_image();
		}
		else
		{
			ImGui::Separator();
			if(ImGui::Button("Cancel##mount"))
			{
				ImGui::CloseCurrentPopup();
				m_create_open = false;
			}
			ImGui::SameLine();
			if(ImGui::Button("OK##mount"))
			{
				auto entry = osd_stat(m_path);
				auto file_type = (entry != nullptr) ? entry->type : directory::entry::entry_type::NONE;
				if(file_type == directory::entry::entry_type::NONE)
					create_image();
				if(file_type == directory::entry::entry_type::FILE)
					m_create_confirm_wait = true;
				// cannot overwrite a directory, so nothing will be none in that case.
				m_create_open = false;
			}
		}
		ImGui::EndPopup();
	}
}

void debug_imgui::draw_console()
{
	ImGuiWindowFlags flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
	bool show_menu = false;

	if(view_main_disasm == nullptr || view_main_regs == nullptr || view_main_console == nullptr)
		return;

	ImGui::SetNextWindowSize(ImVec2(view_main_regs->width + view_main_disasm->width,view_main_disasm->height + view_main_console->height + ImGui::GetTextLineHeight()*3),ImGuiCond_Once);
	if(ImGui::Begin(view_main_console->title.c_str(), nullptr,flags))
	{
		std::string str;

		if(ImGui::BeginMenuBar())
		{
			if(ImGui::BeginMenu("Debug"))
			{
				show_menu = true;
				if(ImGui::MenuItem("New disassembly window", "Ctrl+D"))
					add_disasm(++m_win_count);
				if(ImGui::MenuItem("New memory window", "Ctrl+M"))
					add_memory(++m_win_count);
				if(ImGui::MenuItem("New breakpoints window", "Ctrl+B"))
					add_bpoints(++m_win_count);
				if(ImGui::MenuItem("New watchpoints window", "Ctrl+W"))
					add_wpoints(++m_win_count);
				if(ImGui::MenuItem("New log window", "Ctrl+L"))
					add_log(++m_win_count);
				ImGui::Separator();
				if(ImGui::MenuItem("Run", "F5"))
				{
					m_machine->debugger().console().get_visible_cpu()->debug()->go();
					m_running = true;
				}
				if(ImGui::MenuItem("Go to next CPU", "F6"))
				{
					m_machine->debugger().console().get_visible_cpu()->debug()->go_next_device();
					m_running = true;
				}
				if(ImGui::MenuItem("Run until next interrupt", "F7"))
				{
					m_machine->debugger().console().get_visible_cpu()->debug()->go_interrupt();
					m_running = true;
				}
				if(ImGui::MenuItem("Run until VBLANK", "F8"))
					m_machine->debugger().console().get_visible_cpu()->debug()->go_vblank();
				if(ImGui::MenuItem("Run and hide debugger", "F12"))
				{
					m_machine->debugger().console().get_visible_cpu()->debug()->go();
					m_hide = true;
				}
				ImGui::Separator();
				if(ImGui::MenuItem("Single step", "F11"))
					m_machine->debugger().console().get_visible_cpu()->debug()->single_step();
				if(ImGui::MenuItem("Step over", "F10"))
					m_machine->debugger().console().get_visible_cpu()->debug()->single_step_over();
				if(ImGui::MenuItem("Step out", "F9"))
					m_machine->debugger().console().get_visible_cpu()->debug()->single_step_out();

				ImGui::EndMenu();
			}
			if(ImGui::BeginMenu("Window"))
			{
				show_menu = true;
				if(ImGui::MenuItem("Show all"))
				{
					for(auto view_ptr = view_list.begin();view_ptr != view_list.end();++view_ptr)
						ImGui::SetWindowCollapsed((*view_ptr)->title.c_str(),false);
				}
				ImGui::Separator();
				// list all extra windows, so we can un-collapse the windows if necessary
				for(auto view_ptr = view_list.begin();view_ptr != view_list.end();++view_ptr)
				{
					bool collapsed = false;
					if((*view_ptr)->is_collapsed)
						collapsed = true;
					if(ImGui::MenuItem((*view_ptr)->title.c_str(), nullptr,!collapsed))
						ImGui::SetWindowCollapsed((*view_ptr)->title.c_str(),false);
				}
				ImGui::EndMenu();
			}
			if(m_has_images)
			{
				show_menu = true;
				draw_images_menu();
			}
			ImGui::EndMenuBar();
		}

		// CPU state portion
		ImGui::BeginChild("##state_output", ImVec2(180,ImGui::GetWindowHeight() - ImGui::GetTextLineHeight()*4));  // account for title bar and menu
		draw_view(view_main_regs,false);
		ImGui::EndChild();

		ImGui::SameLine();

		ImGui::BeginChild("##right_side", ImVec2(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - 8,ImGui::GetWindowHeight() - ImGui::GetTextLineHeight()*2));
		// disassembly portion
		ImGui::BeginChild("##disasm_output", ImVec2(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - 8,(ImGui::GetWindowHeight() - ImGui::GetTextLineHeight()*4)/2));
		draw_view(view_main_disasm,false);
		ImGui::EndChild();

		ImGui::Separator();

		// console portion
		ImGui::BeginChild("##console_output", ImVec2(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - 8,(ImGui::GetWindowHeight() - ImGui::GetTextLineHeight()*4)/2 - ImGui::GetTextLineHeight()));
		draw_view(view_main_console,false);
		ImGui::EndChild();
		ImGui::Separator();

		ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackHistory;
		if(m_running)
			flags |= ImGuiInputTextFlags_ReadOnly;
		ImGui::PushItemWidth(-1.0f);
		if(ImGui::InputText("##console_input",view_main_console->console_input,512,flags,history_set))
			view_main_console->exec_cmd = true;
		if ((ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0) && !show_menu))
			ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
		if(m_mount_open)
		{
			ImGui::OpenPopup("Mount Image");
			draw_mount_dialog("Mount Image");  // draw mount image dialog if open
		}
		if(m_create_open)
		{
			ImGui::OpenPopup("Create Image");
			draw_create_dialog("Create Image");  // draw create image dialog if open
		}
		ImGui::PopItemWidth();
		ImGui::EndChild();
		ImGui::End();
	}
}

void debug_imgui::update()
{
	debug_area* to_delete = nullptr;
	//debug_area* view_ptr = view_list;
	std::vector<debug_area*>::iterator view_ptr;
	bool opened;
	ImGui::PushStyleColor(ImGuiCol_WindowBg,ImVec4(1.0f,1.0f,1.0f,0.9f));
	ImGui::PushStyleColor(ImGuiCol_Text,ImVec4(0.0f,0.0f,0.0f,1.0f));
	ImGui::PushStyleColor(ImGuiCol_TextDisabled,ImVec4(0.0f,0.0f,1.0f,1.0f));
	ImGui::PushStyleColor(ImGuiCol_MenuBarBg,ImVec4(0.5f,0.5f,0.5f,0.8f));
	ImGui::PushStyleColor(ImGuiCol_TitleBg,ImVec4(0.6f,0.6f,0.8f,0.8f));
	ImGui::PushStyleColor(ImGuiCol_TitleBgActive,ImVec4(0.7f,0.7f,0.95f,0.8f));
	ImGui::PushStyleColor(ImGuiCol_FrameBg,ImVec4(0.5f,0.5f,0.5f,0.8f));
	ImGui::PushStyleColor(ImGuiCol_PopupBg,ImVec4(0.8f,0.8f,0.8f,0.8f));
	ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab,ImVec4(0.6f,0.6f,0.6f,0.8f));
	ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered,ImVec4(0.7f,0.7f,0.7f,0.8f));
	ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive,ImVec4(0.9f,0.9f,0.9f,0.8f));
	ImGui::PushStyleColor(ImGuiCol_Border,ImVec4(0.7f,0.7f,0.7f,0.8f));
	m_text_size = ImGui::CalcTextSize("A");  // hopefully you're using a monospaced font...
	draw_console();  // We'll always have a console window

	view_ptr = view_list.begin();
	while(view_ptr != view_list.end())
	{
		opened = true;
		switch((*view_ptr)->type)
		{
		case DVT_DISASSEMBLY:
			draw_disasm((*view_ptr),&opened);
			if(opened == false)
				to_delete = (*view_ptr);
			break;
		case DVT_MEMORY:
			draw_memory((*view_ptr),&opened);
			if(opened == false)
				to_delete = (*view_ptr);
			break;
		case DVT_LOG:
			draw_log((*view_ptr),&opened);
			if(opened == false)
				to_delete = (*view_ptr);
			break;
		case DVT_BREAK_POINTS:
		case DVT_WATCH_POINTS:  // watchpoints window uses same drawing code as breakpoints window
			draw_bpoints((*view_ptr),&opened);
			if(opened == false)
				to_delete = (*view_ptr);
			break;
		}
		++view_ptr;
	}
	// check for a closed window
	if(to_delete != nullptr)
	{
		view_list_remove(to_delete);
		delete to_delete;
	}

	ImGui::PopStyleColor(12);
}

void debug_imgui::init_debugger(running_machine &machine)
{
	ImGuiIO& io = ImGui::GetIO();
	m_machine = &machine;
	m_mouse_button = false;
	if(strcmp(downcast<osd_options &>(m_machine->options()).video(),"bgfx") != 0)
		fatalerror("Error: ImGui debugger requires the BGFX renderer.\n");

	// check for any image devices (cassette, floppy, etc...)
	image_interface_enumerator iter(m_machine->root_device());
	if (iter.first() != nullptr)
		m_has_images = true;

	// map keys to ImGui inputs
	m_mapping[ITEM_ID_A] = ImGuiKey_A;
	m_mapping[ITEM_ID_C] = ImGuiKey_C;
	m_mapping[ITEM_ID_V] = ImGuiKey_V;
	m_mapping[ITEM_ID_X] = ImGuiKey_X;
	m_mapping[ITEM_ID_Y] = ImGuiKey_Y;
	m_mapping[ITEM_ID_Z] = ImGuiKey_Z;
	m_mapping[ITEM_ID_D] = ImGuiKey_D;
	m_mapping[ITEM_ID_M] = ImGuiKey_M;
	m_mapping[ITEM_ID_B] = ImGuiKey_B;
	m_mapping[ITEM_ID_W] = ImGuiKey_W;
	m_mapping[ITEM_ID_L] = ImGuiKey_L;
	m_mapping[ITEM_ID_BACKSPACE] = ImGuiKey_Backspace;
	m_mapping[ITEM_ID_DEL] = ImGuiKey_Delete;
	m_mapping[ITEM_ID_TAB] = ImGuiKey_Tab;
	m_mapping[ITEM_ID_PGUP] = ImGuiKey_PageUp;
	m_mapping[ITEM_ID_PGDN] = ImGuiKey_PageDown;
	m_mapping[ITEM_ID_HOME] = ImGuiKey_Home;
	m_mapping[ITEM_ID_END] = ImGuiKey_End;
	m_mapping[ITEM_ID_ESC] = ImGuiKey_Escape;
	m_mapping[ITEM_ID_ENTER] = ImGuiKey_Enter;
	m_mapping[ITEM_ID_LEFT] = ImGuiKey_LeftArrow;
	m_mapping[ITEM_ID_RIGHT] = ImGuiKey_RightArrow;
	m_mapping[ITEM_ID_UP] = ImGuiKey_UpArrow;
	m_mapping[ITEM_ID_DOWN] = ImGuiKey_DownArrow;
	m_mapping[ITEM_ID_F3] = ImGuiKey_F3;
	m_mapping[ITEM_ID_F5] = ImGuiKey_F5;
	m_mapping[ITEM_ID_F6] = ImGuiKey_F6;
	m_mapping[ITEM_ID_F7] = ImGuiKey_F7;
	m_mapping[ITEM_ID_F8] = ImGuiKey_F8;
	m_mapping[ITEM_ID_F9] = ImGuiKey_F9;
	m_mapping[ITEM_ID_F10] = ImGuiKey_F10;
	m_mapping[ITEM_ID_F11] = ImGuiKey_F11;
	m_mapping[ITEM_ID_F12] = ImGuiKey_F12;

	// set key delay and repeat rates
	io.KeyRepeatDelay = 0.400f;
	io.KeyRepeatRate = 0.050f;

	font_name = (downcast<osd_options &>(m_machine->options()).debugger_font());
	font_size = (downcast<osd_options &>(m_machine->options()).debugger_font_size());

	if(font_size == 0)
		font_size = 12;

	io.Fonts->Clear();
	if(!strcmp(font_name, OSDOPTVAL_AUTO))
		io.Fonts->AddFontDefault();
	else
		io.Fonts->AddFontFromFileTTF(font_name,font_size);  // for now, font name must be a path to a TTF file
	imguiCreate();
}

void debug_imgui::wait_for_debugger(device_t &device, bool firststop)
{
	uint32_t width = m_machine->render().ui_target().width();
	uint32_t height = m_machine->render().ui_target().height();
	if(firststop && !m_initialised)
	{
		view_main_console = dview_alloc(device.machine(), DVT_CONSOLE);
		view_main_console->title = "MAME Debugger";
		view_main_console->width = 500;
		view_main_console->height = 200;
		view_main_console->ofs_x = 0;
		view_main_console->ofs_y = 0;
		view_main_console->scroll_follow = true;
		view_main_disasm = dview_alloc(device.machine(), DVT_DISASSEMBLY);
		view_main_disasm->title = "Main Disassembly";
		view_main_disasm->width = 500;
		view_main_disasm->height = 200;
		view_main_regs = dview_alloc(device.machine(), DVT_STATE);
		view_main_regs->title = "Main State";
		view_main_regs->width = 180;
		view_main_regs->height = 440;
		strcpy(view_main_console->console_input,"");  // clear console input
		m_initialised = true;
	}
	if(firststop)
	{
		//debug_show_all();
		m_running = false;
	}
	if(!m_take_ui)
	{
		if (!m_machine->ui().set_ui_event_handler([this] () { return m_take_ui; }))
		{
			// can't break if we can't take over UI input
			m_machine->debugger().console().get_visible_cpu()->debug()->go();
			m_running = true;
			return;
		}
		m_take_ui = true;

	}
	m_hide = false;
	m_machine->osd().input_update(false);
	handle_events();
	handle_console(m_machine);
	update_cpu_view(&device);
	imguiBeginFrame(m_mouse_x, m_mouse_y, m_mouse_button ? IMGUI_MBUT_LEFT : 0, 0, width, height,m_key_char);
	handle_mouse_views();
	handle_keys_views();
	update();
	imguiEndFrame();
	device.machine().osd().update(false);
	osd_sleep(osd_ticks_per_second() / 1000 * 50);
}


void debug_imgui::debugger_update()
{
	if(!view_main_disasm || !view_main_regs || !view_main_console || !m_machine || (m_machine->phase() != machine_phase::RUNNING))
		return;

	if(!m_machine->debugger().cpu().is_stopped())
	{
		if(m_take_ui)
		{
			m_take_ui = false;
			m_current_pointer = -1;
			m_prev_mouse_button = m_mouse_button;
			if(m_mouse_button)
			{
				m_mouse_button = false;
				ImGuiIO& io = ImGui::GetIO();
				io.MouseDown[0] = false;
			}
		}
		if(!m_hide)
		{
			uint32_t width = m_machine->render().ui_target().width();
			uint32_t height = m_machine->render().ui_target().height();
			imguiBeginFrame(m_mouse_x, m_mouse_y, 0, 0, width, height, m_key_char);
			update();
			imguiEndFrame();
		}
	}
}

} // anonymous namespace

} // namespace osd

MODULE_DEFINITION(DEBUG_IMGUI, osd::debug_imgui)