summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/SDL2/include/SDL_thread.h
blob: 377e6c73d163b01b526573964faad9d3c680c7a8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*
  Simple DirectMedia Layer
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
*/

#ifndef _SDL_thread_h
#define _SDL_thread_h

/**
 *  \file SDL_thread.h
 *
 *  Header for the SDL thread management routines.
 */

#include "SDL_stdinc.h"
#include "SDL_error.h"

/* Thread synchronization primitives */
#include "SDL_atomic.h"
#include "SDL_mutex.h"

#include "begin_code.h"
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif

/* The SDL thread structure, defined in SDL_thread.c */
struct SDL_Thread;
typedef struct SDL_Thread SDL_Thread;

/* The SDL thread ID */
typedef unsigned long SDL_threadID;

/* Thread local storage ID, 0 is the invalid ID */
typedef unsigned int SDL_TLSID;

/**
 *  The SDL thread priority.
 *
 *  \note On many systems you require special privileges to set high priority.
 */
typedef enum {
    SDL_THREAD_PRIORITY_LOW,
    SDL_THREAD_PRIORITY_NORMAL,
    SDL_THREAD_PRIORITY_HIGH
} SDL_ThreadPriority;

/**
 *  The function passed to SDL_CreateThread().
 *  It is passed a void* user context parameter and returns an int.
 */
typedef int (SDLCALL * SDL_ThreadFunction) (void *data);

#if defined(__WIN32__) && !defined(HAVE_LIBC)
/**
 *  \file SDL_thread.h
 *
 *  We compile SDL into a DLL. This means, that it's the DLL which
 *  creates a new thread for the calling process with the SDL_CreateThread()
 *  API. There is a problem with this, that only the RTL of the SDL.DLL will
 *  be initialized for those threads, and not the RTL of the calling
 *  application!
 *
 *  To solve this, we make a little hack here.
 *
 *  We'll always use the caller's _beginthread() and _endthread() APIs to
 *  start a new thread. This way, if it's the SDL.DLL which uses this API,
 *  then the RTL of SDL.DLL will be used to create the new thread, and if it's
 *  the application, then the RTL of the application will be used.
 *
 *  So, in short:
 *  Always use the _beginthread() and _endthread() of the calling runtime
 *  library!
 */
#define SDL_PASSED_BEGINTHREAD_ENDTHREAD
#include <process.h>            /* This has _beginthread() and _endthread() defined! */

typedef uintptr_t(__cdecl * pfnSDL_CurrentBeginThread) (void *, unsigned,
                                                        unsigned (__stdcall *
                                                                  func) (void
                                                                         *),
                                                        void *arg, unsigned,
                                                        unsigned *threadID);
typedef void (__cdecl * pfnSDL_CurrentEndThread) (unsigned code);

/**
 *  Create a thread.
 */
extern DECLSPEC SDL_Thread *SDLCALL
SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data,
                 pfnSDL_CurrentBeginThread pfnBeginThread,
                 pfnSDL_CurrentEndThread pfnEndThread);

/**
 *  Create a thread.
 */
#if defined(SDL_CreateThread) && SDL_DYNAMIC_API
#undef SDL_CreateThread
#define SDL_CreateThread(fn, name, data) SDL_CreateThread_REAL(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthreadex, (pfnSDL_CurrentEndThread)_endthreadex)
#else
#define SDL_CreateThread(fn, name, data) SDL_CreateThread(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthreadex, (pfnSDL_CurrentEndThread)_endthreadex)
#endif

#else

/**
 *  Create a thread.
 *
 *   Thread naming is a little complicated: Most systems have very small
 *    limits for the string length (Haiku has 32 bytes, Linux currently has 16,
 *    Visual C++ 6.0 has nine!), and possibly other arbitrary rules. You'll
 *    have to see what happens with your system's debugger. The name should be
 *    UTF-8 (but using the naming limits of C identifiers is a better bet).
 *   There are no requirements for thread naming conventions, so long as the
 *    string is null-terminated UTF-8, but these guidelines are helpful in
 *    choosing a name:
 *
 *    http://stackoverflow.com/questions/149932/naming-conventions-for-threads
 *
 *   If a system imposes requirements, SDL will try to munge the string for
 *    it (truncate, etc), but the original string contents will be available
 *    from SDL_GetThreadName().
 */
extern DECLSPEC SDL_Thread *SDLCALL
SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data);

#endif

/**
 * Get the thread name, as it was specified in SDL_CreateThread().
 *  This function returns a pointer to a UTF-8 string that names the
 *  specified thread, or NULL if it doesn't have a name. This is internal
 *  memory, not to be free()'d by the caller, and remains valid until the
 *  specified thread is cleaned up by SDL_WaitThread().
 */
extern DECLSPEC const char *SDLCALL SDL_GetThreadName(SDL_Thread *thread);

/**
 *  Get the thread identifier for the current thread.
 */
extern DECLSPEC SDL_threadID SDLCALL SDL_ThreadID(void);

/**
 *  Get the thread identifier for the specified thread.
 *
 *  Equivalent to SDL_ThreadID() if the specified thread is NULL.
 */
extern DECLSPEC SDL_threadID SDLCALL SDL_GetThreadID(SDL_Thread * thread);

/**
 *  Set the priority for the current thread
 */
extern DECLSPEC int SDLCALL SDL_SetThreadPriority(SDL_ThreadPriority priority);

/**
 *  Wait for a thread to finish. Threads that haven't been detached will
 *  remain (as a "zombie") until this function cleans them up. Not doing so
 *  is a resource leak.
 *
 *  Once a thread has been cleaned up through this function, the SDL_Thread
 *  that references it becomes invalid and should not be referenced again.
 *  As such, only one thread may call SDL_WaitThread() on another.
 *
 *  The return code for the thread function is placed in the area
 *  pointed to by \c status, if \c status is not NULL.
 *
 *  You may not wait on a thread that has been used in a call to
 *  SDL_DetachThread(). Use either that function or this one, but not
 *  both, or behavior is undefined.
 *
 *  It is safe to pass NULL to this function; it is a no-op.
 */
extern DECLSPEC void SDLCALL SDL_WaitThread(SDL_Thread * thread, int *status);

/**
 *  A thread may be "detached" to signify that it should not remain until
 *  another thread has called SDL_WaitThread() on it. Detaching a thread
 *  is useful for long-running threads that nothing needs to synchronize
 *  with or further manage. When a detached thread is done, it simply
 *  goes away.
 *
 *  There is no way to recover the return code of a detached thread. If you
 *  need this, don't detach the thread and instead use SDL_WaitThread().
 *
 *  Once a thread is detached, you should usually assume the SDL_Thread isn't
 *  safe to reference again, as it will become invalid immediately upon
 *  the detached thread's exit, instead of remaining until someone has called
 *  SDL_WaitThread() to finally clean it up. As such, don't detach the same
 *  thread more than once.
 *
 *  If a thread has already exited when passed to SDL_DetachThread(), it will
 *  stop waiting for a call to SDL_WaitThread() and clean up immediately.
 *  It is not safe to detach a thread that might be used with SDL_WaitThread().
 *
 *  You may not call SDL_WaitThread() on a thread that has been detached.
 *  Use either that function or this one, but not both, or behavior is
 *  undefined.
 *
 *  It is safe to pass NULL to this function; it is a no-op.
 */
extern DECLSPEC void SDLCALL SDL_DetachThread(SDL_Thread * thread);

/**
 *  \brief Create an identifier that is globally visible to all threads but refers to data that is thread-specific.
 *
 *  \return The newly created thread local storage identifier, or 0 on error
 *
 *  \code
 *  static SDL_SpinLock tls_lock;
 *  static SDL_TLSID thread_local_storage;
 * 
 *  void SetMyThreadData(void *value)
 *  {
 *      if (!thread_local_storage) {
 *          SDL_AtomicLock(&tls_lock);
 *          if (!thread_local_storage) {
 *              thread_local_storage = SDL_TLSCreate();
 *          }
 *          SDL_AtomicUnlock(&tls_lock);
 *      }
 *      SDL_TLSSet(thread_local_storage, value, 0);
 *  }
 *  
 *  void *GetMyThreadData(void)
 *  {
 *      return SDL_TLSGet(thread_local_storage);
 *  }
 *  \endcode
 *
 *  \sa SDL_TLSGet()
 *  \sa SDL_TLSSet()
 */
extern DECLSPEC SDL_TLSID SDLCALL SDL_TLSCreate(void);

/**
 *  \brief Get the value associated with a thread local storage ID for the current thread.
 *
 *  \param id The thread local storage ID
 *
 *  \return The value associated with the ID for the current thread, or NULL if no value has been set.
 *
 *  \sa SDL_TLSCreate()
 *  \sa SDL_TLSSet()
 */
extern DECLSPEC void * SDLCALL SDL_TLSGet(SDL_TLSID id);

/**
 *  \brief Set the value associated with a thread local storage ID for the current thread.
 *
 *  \param id The thread local storage ID
 *  \param value The value to associate with the ID for the current thread
 *  \param destructor A function called when the thread exits, to free the value.
 *
 *  \return 0 on success, -1 on error
 *
 *  \sa SDL_TLSCreate()
 *  \sa SDL_TLSGet()
 */
extern DECLSPEC int SDLCALL SDL_TLSSet(SDL_TLSID id, const void *value, void (*destructor)(void*));


/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include "close_code.h"

#endif /* _SDL_thread_h */

/* vi: set ts=4 sw=4 expandtab: */
Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
// ImGui based debugger

#include "emu.h"
#include "imgui/imgui.h"
#include "render.h"
#include "uiinput.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 "config.h"
#include "debugger.h"
#include "modules/lib/osdobj_common.h"
#include "debug_module.h"
#include "modules/osdmodule.h"

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),
			exec_cmd(false)
		{
		this->view = machine.debug_view().alloc_view(type, nullptr, this);
		this->type = type;
		this->m_machine = &machine;
		this->width = 300;
		this->height = 300;

		/* 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                exec_cmd;  // console only
	int                 src_sel;
	char                console_input[512];
};

class debug_imgui : public osd_module, public debug_module
{
public:
	debug_imgui()
	: osd_module(OSD_DEBUG_PROVIDER, "imgui"), debug_module(),
		m_machine(nullptr),
		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)
	{
	}

	virtual ~debug_imgui() { }

	virtual int init(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:
	void handle_mouse();
	void handle_mouse_views();
	void handle_keys();
	void handle_keys_views();
	void handle_console(running_machine* machine);
	void update();
	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 update_cpu_view(device_t* device);
	static bool get_view_source(void* data, int idx, const char** out_text);

	running_machine* m_machine;
	INT32            m_mouse_x;
	INT32            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)
	ImguiFontHandle  m_font;
	UINT8            m_key_char;
	bool             m_hide;
	int              m_win_count;  // number of active windows, does not decrease, used to ID individual windows
};

// 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 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)
{
	debug_area *dv;

	dv = global_alloc(debug_area(machine, type));

	return dv;
}

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)
{
	debug_view* vw = static_cast<debug_view*>(data);
	*out_text = vw->source_list().find(idx)->name();
	return true;
}

void debug_imgui::handle_mouse()
{
	m_prev_mouse_button = m_mouse_button;
	m_machine->ui_input().find_mouse(&m_mouse_x, &m_mouse_y, &m_mouse_button);
}

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(std::vector<debug_area*>::iterator 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()
{
	ImGuiIO& io = ImGui::GetIO();
	ui_event event;

	// global keys
	if(m_machine->input().code_pressed_once(KEYCODE_F3))
	{
		if(m_machine->input().code_pressed(KEYCODE_LSHIFT))
			m_machine->schedule_hard_reset();
		else
		{
			m_machine->schedule_soft_reset();
			debug_cpu_get_visible_cpu(*m_machine)->debug()->go();
		}
	}

	if(m_machine->input().code_pressed_once(KEYCODE_F5))
	{
		debug_cpu_get_visible_cpu(*m_machine)->debug()->go();
		m_running = true;
	}
	if(m_machine->input().code_pressed_once(KEYCODE_F6))
	{
		debug_cpu_get_visible_cpu(*m_machine)->debug()->go_next_device();
		m_running = true;
	}
	if(m_machine->input().code_pressed_once(KEYCODE_F7))
	{
		debug_cpu_get_visible_cpu(*m_machine)->debug()->go_interrupt();
		m_running = true;
	}
	if(m_machine->input().code_pressed_once(KEYCODE_F8))
		debug_cpu_get_visible_cpu(*m_machine)->debug()->go_vblank();
	if(m_machine->input().code_pressed_once(KEYCODE_F9))
		debug_cpu_get_visible_cpu(*m_machine)->debug()->single_step_out();
	if(m_machine->input().code_pressed_once(KEYCODE_F10))
		debug_cpu_get_visible_cpu(*m_machine)->debug()->single_step_over();
	if(m_machine->input().code_pressed_once(KEYCODE_F11))
		debug_cpu_get_visible_cpu(*m_machine)->debug()->single_step();
	if(m_machine->input().code_pressed_once(KEYCODE_F12))
	{
		debug_cpu_get_visible_cpu(*m_machine)->debug()->go();
		m_hide = true;
	}

/*  if(m_machine->input().code_pressed_once(KEYCODE_UP))
        io.KeysDown[ImGuiKey_UpArrow] = true;
    if(m_machine->input().code_pressed_once(KEYCODE_DOWN))
        io.KeysDown[ImGuiKey_DownArrow] = true;
    if(m_machine->input().code_pressed_once(KEYCODE_LEFT))
        io.KeysDown[ImGuiKey_LeftArrow] = true;
    if(m_machine->input().code_pressed_once(KEYCODE_RIGHT))
        io.KeysDown[ImGuiKey_RightArrow] = true;

    if(m_machine->input().code_pressed(KEYCODE_TAB))
        io.KeysDown[ImGuiKey_Tab] = true;

    if(m_machine->input().code_pressed_once(KEYCODE_PGUP))
    {
        io.KeysDown[ImGuiKey_PageUp] = true;
    }
    if(m_machine->input().code_pressed_once(KEYCODE_PGDN))
    {
        io.KeysDown[ImGuiKey_PageDown] = true;
    }

    if(m_machine->input().code_pressed_once(KEYCODE_HOME))
    {
        io.KeysDown[ImGuiKey_Home] = true;
    }
    if(m_machine->input().code_pressed_once(KEYCODE_END))
    {
        io.KeysDown[ImGuiKey_End] = true;
    }*/
	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)))
			io.KeysDown[id] = true;
		else
			io.KeysDown[id] = false;
	}

	m_key_char = 0;
	while (m_machine->ui_input().pop_event(&event))
	{
		switch (event.event_type)
		{
		case UI_EVENT_CHAR:
			m_key_char = event.ch;
			return;
		default:
			break;
		}
	}

	if(ImGui::IsKeyPressed(ITEM_ID_D) && ImGui::IsKeyDown(ITEM_ID_LCONTROL))
		add_disasm(++m_win_count);
	if(ImGui::IsKeyPressed(ITEM_ID_M) && ImGui::IsKeyDown(ITEM_ID_LCONTROL))
		add_memory(++m_win_count);
	if(ImGui::IsKeyPressed(ITEM_ID_B) && ImGui::IsKeyDown(ITEM_ID_LCONTROL))
		add_bpoints(++m_win_count);
	if(ImGui::IsKeyPressed(ITEM_ID_W) && ImGui::IsKeyDown(ITEM_ID_LCONTROL))
		add_wpoints(++m_win_count);
	if(ImGui::IsKeyPressed(ITEM_ID_L) && ImGui::IsKeyDown(ITEM_ID_LCONTROL))
		add_log(++m_win_count);

}

void debug_imgui::handle_keys_views()
{
	debug_area* focus_view = nullptr;
	// find view that has focus (should only be one at a time)
	for(std::vector<debug_area*>::iterator 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->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(m_machine->input().code_pressed_once(KEYCODE_UP))
		focus_view->view->process_char(DCH_UP);
	if(m_machine->input().code_pressed_once(KEYCODE_DOWN))
		focus_view->view->process_char(DCH_DOWN);
	if(m_machine->input().code_pressed_once(KEYCODE_LEFT))
	{
		if(m_machine->input().code_pressed(KEYCODE_LCONTROL))
			focus_view->view->process_char(DCH_CTRLLEFT);
		else
			focus_view->view->process_char(DCH_LEFT);
	}
	if(m_machine->input().code_pressed_once(KEYCODE_RIGHT))
	{
		if(m_machine->input().code_pressed(KEYCODE_LCONTROL))
			focus_view->view->process_char(DCH_CTRLRIGHT);
		else
			focus_view->view->process_char(DCH_RIGHT);
	}
	if(m_machine->input().code_pressed_once(KEYCODE_PGUP))
		focus_view->view->process_char(DCH_PUP);
	if(m_machine->input().code_pressed_once(KEYCODE_PGDN))
		focus_view->view->process_char(DCH_PDOWN);
	if(m_machine->input().code_pressed_once(KEYCODE_HOME))
	{
		if(m_machine->input().code_pressed(KEYCODE_LCONTROL))
			focus_view->view->process_char(DCH_CTRLHOME);
		else
			focus_view->view->process_char(DCH_HOME);
	}
	if(m_machine->input().code_pressed_once(KEYCODE_END))
	{
		if(m_machine->input().code_pressed(KEYCODE_LCONTROL))
			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(strlen(view_main_console->console_input) > 0)
			debug_console_execute_command(*m_machine, view_main_console->console_input, 1);
		// 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;
		strcpy(view_main_console->console_input,"");
		view_main_console->exec_cmd = false;
	}
}

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
	if(exp_change)
		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
	ImGui::SetCursorPosY((totalsize.y) * fsize.y);

	// 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
	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 = ImGui::ColorConvertFloat4ToU32(ImVec4(fg.r()/255.0f,fg.g()/255.0f,fg.b()/255.0f,fg.a()/255.0f));
			str[0] = v = viewdata->byte;
			str[1] = '\0';
			if(bg != base)
			{
				ImU32 bg_col = ImGui::ColorConvertFloat4ToU32(ImVec4(bg.r()/255.0f,bg.g()/255.0f,bg.b()/255.0f,bg.a()/255.0f));
				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;
	}
	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()),ImGuiSetCond_Once);
	if(ImGui::Begin(view_ptr->title.c_str(),opened))
	{
		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();
	}
}

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()),ImGuiSetCond_Once);
	if(ImGui::Begin(view_ptr->title.c_str(),opened))
	{
		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();
	}
}

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;
	view_list_add(new_view);
}

void debug_imgui::draw_disasm(debug_area* view_ptr, bool* opened)
{
	const debug_view_source* src;

	ImGui::SetNextWindowSize(ImVec2(view_ptr->width,view_ptr->height + ImGui::GetTextLineHeight()),ImGuiSetCond_Once);
	if(ImGui::Begin(view_ptr->title.c_str(),opened,ImGuiWindowFlags_MenuBar))
	{
		int idx;
		bool done = false;
		bool exp_change = false;

		if(ImGui::BeginMenuBar())
		{
			if(ImGui::BeginMenu("Options"))
			{
				debug_view_disasm* 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;
		if(m_running)
			flags |= ImGuiInputTextFlags_ReadOnly;
		ImGui::Combo("##cpu",&view_ptr->src_sel,get_view_source,view_ptr->view,view_ptr->view->source_list().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
		src = view_ptr->view->first_source();
		idx = 0;
		while (!done)
		{
			if(view_ptr->src_sel == idx)
				view_ptr->view->set_source(*src);
			idx++;
			src = src->next();
			if(src == nullptr)
				done = true;
		}

		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();
	}
}

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)
{
	const debug_view_source* src;

	ImGui::SetNextWindowSize(ImVec2(view_ptr->width,view_ptr->height + ImGui::GetTextLineHeight()),ImGuiSetCond_Once);
	if(ImGui::Begin(view_ptr->title.c_str(),opened,ImGuiWindowFlags_MenuBar))
	{
		int idx;
		bool done = false;
		bool exp_change = false;

		if(ImGui::BeginMenuBar())
		{
			if(ImGui::BeginMenu("Options"))
			{
				debug_view_memory* mem = downcast<debug_view_memory*>(view_ptr->view);
				bool physical = mem->physical();
				bool rev = mem->reverse();
				int format = mem->get_data_format();
				UINT32 chunks = mem->chunks_per_row();

				if(ImGui::MenuItem("1-byte chunks", nullptr,(format == 1) ? true : false))
					mem->set_data_format(1);
				if(ImGui::MenuItem("2-byte chunks", nullptr,(format == 2) ? true : false))
					mem->set_data_format(2);
				if(ImGui::MenuItem("4-byte chunks", nullptr,(format == 4) ? true : false))
					mem->set_data_format(4);
				if(ImGui::MenuItem("8-byte chunks", nullptr,(format == 8) ? true : false))
					mem->set_data_format(8);
				if(ImGui::MenuItem("32-bit floating point", nullptr,(format == 9) ? true : false))
					mem->set_data_format(9);
				if(ImGui::MenuItem("64-bit floating point", nullptr,(format == 10) ? true : false))
					mem->set_data_format(10);
				if(ImGui::MenuItem("80-bit floating point", nullptr,(format == 11) ? true : false))
					mem->set_data_format(11);
				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;
		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_list().count());
		ImGui::PopItemWidth();
		ImGui::Separator();

		// memory editor portion
		src = view_ptr->view->first_source();
		idx = 0;
		while (!done)
		{
			if(view_ptr->src_sel == idx)
				view_ptr->view->set_source(*src);
			idx++;
			src = src->next();
			if(src == nullptr)
				done = true;
		}

		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();
	}
}

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::draw_console()
{
	rgb_t bg, fg;
	rgb_t base(0xe6, 0xff, 0xff, 0xff);
	ImGuiWindowFlags flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;

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

		if(ImGui::BeginMenuBar())
		{
			if(ImGui::BeginMenu("Debug"))
			{
				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"))
				{
					debug_cpu_get_visible_cpu(*m_machine)->debug()->go();
					m_running = true;
				}
				if(ImGui::MenuItem("Go to next CPU", "F6"))
				{
					debug_cpu_get_visible_cpu(*m_machine)->debug()->go_next_device();
					m_running = true;
				}
				if(ImGui::MenuItem("Run until next interrupt", "F7"))
				{
					debug_cpu_get_visible_cpu(*m_machine)->debug()->go_interrupt();
					m_running = true;
				}
				if(ImGui::MenuItem("Run until VBLANK", "F8"))
					debug_cpu_get_visible_cpu(*m_machine)->debug()->go_vblank();
				if(ImGui::MenuItem("Run and hide debugger", "F12"))
				{
					debug_cpu_get_visible_cpu(*m_machine)->debug()->go();
					m_hide = true;
				}
				ImGui::Separator();
				if(ImGui::MenuItem("Single step", "F11"))
					debug_cpu_get_visible_cpu(*m_machine)->debug()->single_step();
				if(ImGui::MenuItem("Step over", "F10"))
					debug_cpu_get_visible_cpu(*m_machine)->debug()->single_step_over();
				if(ImGui::MenuItem("Step out", "F9"))
					debug_cpu_get_visible_cpu(*m_machine)->debug()->single_step_out();

				ImGui::EndMenu();
			}
			if(ImGui::BeginMenu("Window"))
			{
				if(ImGui::MenuItem("Show all"))
				{
					for(std::vector<debug_area*>::iterator 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
				//debug_area* view_ptr;
				for(std::vector<debug_area*>::iterator view_ptr = view_list.begin();view_ptr != view_list.end();++view_ptr)
				{
					bool collapsed;
					if(ImGui::Begin((*view_ptr)->title.c_str()))
					{
						collapsed = false;
						ImGui::End();
					}
					else
					{
						collapsed = true;
						ImGui::End();
					}
					if(ImGui::MenuItem((*view_ptr)->title.c_str(), nullptr,!collapsed))
						ImGui::SetWindowCollapsed((*view_ptr)->title.c_str(),false);
				}
				ImGui::EndMenu();
			}
			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();
		//if(ImGui::IsWindowFocused())
		//  ImGui::SetKeyboardFocusHere();
		ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue;
		if(m_running)
			flags |= ImGuiInputTextFlags_ReadOnly;
		ImGui::PushItemWidth(-1.0f);
		if(ImGui::InputText("##console_input",view_main_console->console_input,512,flags))
			view_main_console->exec_cmd = true;
		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;
	int count = 0;
	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;
		count++;
	}
	// check for a closed window
	if(to_delete != nullptr)
	{
		view_list_remove(to_delete);
		global_free(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");

	io.KeyMap[ImGuiKey_A] = ITEM_ID_A;
	io.KeyMap[ImGuiKey_C] = ITEM_ID_C;
	io.KeyMap[ImGuiKey_V] = ITEM_ID_V;
	io.KeyMap[ImGuiKey_X] = ITEM_ID_X;
	io.KeyMap[ImGuiKey_C] = ITEM_ID_C;
	io.KeyMap[ImGuiKey_Y] = ITEM_ID_Y;
	io.KeyMap[ImGuiKey_Z] = ITEM_ID_Z;
	io.KeyMap[ImGuiKey_Backspace] = ITEM_ID_BACKSPACE;
	io.KeyMap[ImGuiKey_Delete] = ITEM_ID_DEL;
	io.KeyMap[ImGuiKey_Tab] = ITEM_ID_TAB;
	io.KeyMap[ImGuiKey_PageUp] = ITEM_ID_PGUP;
	io.KeyMap[ImGuiKey_PageDown] = ITEM_ID_PGDN;
	io.KeyMap[ImGuiKey_Home] = ITEM_ID_HOME;
	io.KeyMap[ImGuiKey_End] = ITEM_ID_END;
	io.KeyMap[ImGuiKey_Escape] = ITEM_ID_ESC;
	io.KeyMap[ImGuiKey_Enter] = ITEM_ID_ENTER;

	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
	m_font = imguiCreate();
	imguiSetFont(m_font);
}

void debug_imgui::wait_for_debugger(device_t &device, bool firststop)
{
	UINT32 width = m_machine->render().ui_target().width();
	UINT32 height = m_machine->render().ui_target().height();
	if(firststop && view_list.empty())
	{
		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_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
	}
	if(firststop)
	{
//      debug_show_all();
		device.machine().ui_input().reset();
		m_running = false;
	}
	m_hide = false;
//  m_machine->ui_input().frame_update();
	handle_mouse();
	handle_keys();
	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);
	update();
	imguiEndFrame();
	handle_mouse_views();
	handle_keys_views();
	m_machine->ui_input().reset();  // clear remaining inputs, so they don't fall through to the UI
	device.machine().osd().update(false);
}


void debug_imgui::debugger_update()
{
	if ((m_machine != nullptr) && (!debug_cpu_is_stopped(*m_machine)) && (m_machine->phase() == MACHINE_PHASE_RUNNING) && !m_hide)
	{
		UINT32 width = m_machine->render().ui_target().width();
		UINT32 height = m_machine->render().ui_target().height();
		handle_mouse();
		handle_keys();
		imguiBeginFrame(m_mouse_x,m_mouse_y,m_mouse_button ? IMGUI_MBUT_LEFT : 0, 0, width, height, m_key_char);
		update();
		imguiEndFrame();
	}
}

MODULE_DEFINITION(DEBUG_IMGUI, debug_imgui)