From 7b77f1218624ea26dbb2efd85a19f795f5d4e02e Mon Sep 17 00:00:00 2001 From: Aaron Giles Date: Mon, 17 Dec 2007 15:19:59 +0000 Subject: Initial checkin of MAME 0.121. --- src/osd/osdcomm.h | 170 +++ src/osd/osdcore.h | 802 ++++++++++++ src/osd/osdepend.h | 152 +++ src/osd/osdmini/minidir.c | 45 + src/osd/osdmini/minifile.c | 125 ++ src/osd/osdmini/minimisc.c | 53 + src/osd/osdmini/minisync.c | 67 + src/osd/osdmini/minitime.c | 57 + src/osd/osdmini/miniwork.c | 115 ++ src/osd/osdmini/osd_cpu.h | 68 + src/osd/osdmini/osdmini.mak | 23 + src/osd/windows/d3d8intf.c | 557 ++++++++ src/osd/windows/d3d9intf.c | 590 +++++++++ src/osd/windows/d3dintf.h | 241 ++++ src/osd/windows/debugwin.c | 3025 +++++++++++++++++++++++++++++++++++++++++++ src/osd/windows/debugwin.h | 32 + src/osd/windows/drawd3d.c | 2421 ++++++++++++++++++++++++++++++++++ src/osd/windows/drawdd.c | 1435 ++++++++++++++++++++ src/osd/windows/drawgdi.c | 215 +++ src/osd/windows/drawnone.c | 104 ++ src/osd/windows/input.c | 2129 ++++++++++++++++++++++++++++++ src/osd/windows/input.h | 27 + src/osd/windows/ledutil.c | 738 +++++++++++ src/osd/windows/main.c | 89 ++ src/osd/windows/mame.man | 10 + src/osd/windows/mame.rc | 13 + src/osd/windows/mamex64.man | 10 + src/osd/windows/mamex64.rc | 14 + src/osd/windows/multidef.h | 38 + src/osd/windows/osd_cpu.h | 60 + src/osd/windows/osinline.h | 562 ++++++++ src/osd/windows/output.c | 311 +++++ src/osd/windows/output.h | 97 ++ src/osd/windows/sound.c | 427 ++++++ src/osd/windows/sound.h | 20 + src/osd/windows/strconv.c | 108 ++ src/osd/windows/strconv.h | 37 + src/osd/windows/vconv.c | 450 +++++++ src/osd/windows/verinfo.c | 267 ++++ src/osd/windows/video.c | 524 ++++++++ src/osd/windows/video.h | 100 ++ src/osd/windows/winalloc.c | 395 ++++++ src/osd/windows/windir.c | 155 +++ src/osd/windows/window.c | 1885 +++++++++++++++++++++++++++ src/osd/windows/window.h | 160 +++ src/osd/windows/windows.mak | 299 +++++ src/osd/windows/winfile.c | 331 +++++ src/osd/windows/winmain.c | 844 ++++++++++++ src/osd/windows/winmain.h | 68 + src/osd/windows/winmisc.c | 65 + src/osd/windows/winprefix.h | 35 + src/osd/windows/winsync.c | 157 +++ src/osd/windows/wintime.c | 159 +++ src/osd/windows/winutf8.c | 260 ++++ src/osd/windows/winutf8.h | 30 + src/osd/windows/winutil.c | 125 ++ src/osd/windows/winutil.h | 20 + src/osd/windows/winwork.c | 775 +++++++++++ 58 files changed, 22091 insertions(+) create mode 100644 src/osd/osdcomm.h create mode 100644 src/osd/osdcore.h create mode 100644 src/osd/osdepend.h create mode 100644 src/osd/osdmini/minidir.c create mode 100644 src/osd/osdmini/minifile.c create mode 100644 src/osd/osdmini/minimisc.c create mode 100644 src/osd/osdmini/minisync.c create mode 100644 src/osd/osdmini/minitime.c create mode 100644 src/osd/osdmini/miniwork.c create mode 100644 src/osd/osdmini/osd_cpu.h create mode 100644 src/osd/osdmini/osdmini.mak create mode 100644 src/osd/windows/d3d8intf.c create mode 100644 src/osd/windows/d3d9intf.c create mode 100644 src/osd/windows/d3dintf.h create mode 100644 src/osd/windows/debugwin.c create mode 100644 src/osd/windows/debugwin.h create mode 100644 src/osd/windows/drawd3d.c create mode 100644 src/osd/windows/drawdd.c create mode 100644 src/osd/windows/drawgdi.c create mode 100644 src/osd/windows/drawnone.c create mode 100644 src/osd/windows/input.c create mode 100644 src/osd/windows/input.h create mode 100644 src/osd/windows/ledutil.c create mode 100644 src/osd/windows/main.c create mode 100644 src/osd/windows/mame.man create mode 100644 src/osd/windows/mame.rc create mode 100644 src/osd/windows/mamex64.man create mode 100644 src/osd/windows/mamex64.rc create mode 100644 src/osd/windows/multidef.h create mode 100644 src/osd/windows/osd_cpu.h create mode 100644 src/osd/windows/osinline.h create mode 100644 src/osd/windows/output.c create mode 100644 src/osd/windows/output.h create mode 100644 src/osd/windows/sound.c create mode 100644 src/osd/windows/sound.h create mode 100644 src/osd/windows/strconv.c create mode 100644 src/osd/windows/strconv.h create mode 100644 src/osd/windows/vconv.c create mode 100644 src/osd/windows/verinfo.c create mode 100644 src/osd/windows/video.c create mode 100644 src/osd/windows/video.h create mode 100644 src/osd/windows/winalloc.c create mode 100644 src/osd/windows/windir.c create mode 100644 src/osd/windows/window.c create mode 100644 src/osd/windows/window.h create mode 100644 src/osd/windows/windows.mak create mode 100644 src/osd/windows/winfile.c create mode 100644 src/osd/windows/winmain.c create mode 100644 src/osd/windows/winmain.h create mode 100644 src/osd/windows/winmisc.c create mode 100644 src/osd/windows/winprefix.h create mode 100644 src/osd/windows/winsync.c create mode 100644 src/osd/windows/wintime.c create mode 100644 src/osd/windows/winutf8.c create mode 100644 src/osd/windows/winutf8.h create mode 100644 src/osd/windows/winutil.c create mode 100644 src/osd/windows/winutil.h create mode 100644 src/osd/windows/winwork.c (limited to 'src/osd') diff --git a/src/osd/osdcomm.h b/src/osd/osdcomm.h new file mode 100644 index 00000000000..214ed479f7c --- /dev/null +++ b/src/osd/osdcomm.h @@ -0,0 +1,170 @@ +/*************************************************************************** + + osdcomm.h + + Common definitions shared by the OSD layer. This includes the most + fundamental integral types as well as compiler-specific tweaks. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __OSDCOMM_H__ +#define __OSDCOMM_H__ + +#include +#include +#include + + + +/*************************************************************************** + COMPILER-SPECIFIC NASTINESS +***************************************************************************/ + +/* The Win32 port requires this constant for variable arg routines. */ +#ifndef CLIB_DECL +#define CLIB_DECL +#endif + + +/* Some optimizations/warnings cleanups for GCC */ +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define ATTR_UNUSED __attribute__((__unused__)) +#define ATTR_NORETURN __attribute__((noreturn)) +#define ATTR_PRINTF(x,y) __attribute__((format(printf, x, y))) +#define ATTR_MALLOC __attribute__((malloc)) +#define ATTR_PURE __attribute__((pure)) +#define ATTR_CONST __attribute__((const)) +#define UNEXPECTED(exp) __builtin_expect((exp), 0) +#define TYPES_COMPATIBLE(a,b) __builtin_types_compatible_p(a, b) +#define RESTRICT __restrict__ +#else +#define ATTR_UNUSED +#define ATTR_NORETURN +#define ATTR_PRINTF(x,y) +#define ATTR_MALLOC +#define ATTR_PURE +#define ATTR_CONST +#define UNEXPECTED(exp) (exp) +#define TYPES_COMPATIBLE(a,b) 1 +#define RESTRICT +#endif + + +/* And some MSVC optimizations/warnings */ +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#define DECL_NORETURN __declspec(noreturn) +#else +#define DECL_NORETURN +#endif + + + +/*************************************************************************** + FUNDAMENTAL TYPES +***************************************************************************/ + +/* These types work on most modern compilers; however, OSD code can + define their own by setting OSD_TYPES_DEFINED */ + +#ifndef OSD_TYPES_DEFINED + +/* 8-bit values */ +typedef unsigned char UINT8; +typedef signed char INT8; + +/* 16-bit values */ +typedef unsigned short UINT16; +typedef signed short INT16; + +/* 32-bit values */ +#ifndef _WINDOWS_H +typedef unsigned int UINT32; +typedef signed int INT32; +#endif + +/* 64-bit values */ +#ifndef _WINDOWS_H +#ifdef _MSC_VER +typedef signed __int64 INT64; +typedef unsigned __int64 UINT64; +#else +__extension__ typedef unsigned long long UINT64; +__extension__ typedef signed long long INT64; +#endif +#endif + +#endif + + + +/*************************************************************************** + FUNDAMENTAL CONSTANTS +***************************************************************************/ + +/* Ensure that TRUE/FALSE are defined */ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + + + +/*************************************************************************** + FUNDAMENTAL MACROS +***************************************************************************/ + +/* Standard MIN/MAX macros */ +#ifndef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#endif + + +/* Highly useful macro for compile-time knowledge of an array size */ +#define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0])) + + +/* Macros for normalizing data into big or little endian formats */ +#define FLIPENDIAN_INT16(x) (((((UINT16) (x)) >> 8) | ((x) << 8)) & 0xffff) +#define FLIPENDIAN_INT32(x) ((((UINT32) (x)) << 24) | (((UINT32) (x)) >> 24) | \ + (( ((UINT32) (x)) & 0x0000ff00) << 8) | (( ((UINT32) (x)) & 0x00ff0000) >> 8)) +#define FLIPENDIAN_INT64(x) \ + ( \ + (((((UINT64) (x)) >> 56) & ((UINT64) 0xFF)) << 0) | \ + (((((UINT64) (x)) >> 48) & ((UINT64) 0xFF)) << 8) | \ + (((((UINT64) (x)) >> 40) & ((UINT64) 0xFF)) << 16) | \ + (((((UINT64) (x)) >> 32) & ((UINT64) 0xFF)) << 24) | \ + (((((UINT64) (x)) >> 24) & ((UINT64) 0xFF)) << 32) | \ + (((((UINT64) (x)) >> 16) & ((UINT64) 0xFF)) << 40) | \ + (((((UINT64) (x)) >> 8) & ((UINT64) 0xFF)) << 48) | \ + (((((UINT64) (x)) >> 0) & ((UINT64) 0xFF)) << 56) \ + ) + +#ifdef LSB_FIRST +#define BIG_ENDIANIZE_INT16(x) (FLIPENDIAN_INT16(x)) +#define BIG_ENDIANIZE_INT32(x) (FLIPENDIAN_INT32(x)) +#define BIG_ENDIANIZE_INT64(x) (FLIPENDIAN_INT64(x)) +#define LITTLE_ENDIANIZE_INT16(x) (x) +#define LITTLE_ENDIANIZE_INT32(x) (x) +#define LITTLE_ENDIANIZE_INT64(x) (x) +#else +#define BIG_ENDIANIZE_INT16(x) (x) +#define BIG_ENDIANIZE_INT32(x) (x) +#define BIG_ENDIANIZE_INT64(x) (x) +#define LITTLE_ENDIANIZE_INT16(x) (FLIPENDIAN_INT16(x)) +#define LITTLE_ENDIANIZE_INT32(x) (FLIPENDIAN_INT32(x)) +#define LITTLE_ENDIANIZE_INT64(x) (FLIPENDIAN_INT64(x)) +#endif /* LSB_FIRST */ + + +#endif /* __OSDCOMM_H__ */ diff --git a/src/osd/osdcore.h b/src/osd/osdcore.h new file mode 100644 index 00000000000..e5498881e9d --- /dev/null +++ b/src/osd/osdcore.h @@ -0,0 +1,802 @@ +/*************************************************************************** + + osdcore.h + + Core OS-dependent code interface. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + The prototypes in this file describe the interfaces that the MAME core + and various tools rely upon to interact with the outside world. They are + broken out into several categories. + +***************************************************************************/ + +#pragma once + +#ifndef __OSDCORE_H__ +#define __OSDCORE_H__ + +#include "osdcomm.h" + + + +/*************************************************************************** + FILE I/O INTERFACES +***************************************************************************/ + +/* Make sure we have a path separator (default to /) */ +#ifndef PATH_SEPARATOR +#define PATH_SEPARATOR "/" +#endif + +/* flags controlling file access */ +#define OPEN_FLAG_READ 0x0001 /* open for read */ +#define OPEN_FLAG_WRITE 0x0002 /* open for write */ +#define OPEN_FLAG_CREATE 0x0004 /* create & truncate file */ +#define OPEN_FLAG_CREATE_PATHS 0x0008 /* create paths as necessary */ + +/* error codes returned by routines below */ +enum _file_error +{ + FILERR_NONE, + FILERR_FAILURE, + FILERR_OUT_OF_MEMORY, + FILERR_NOT_FOUND, + FILERR_ACCESS_DENIED, + FILERR_ALREADY_OPEN, + FILERR_TOO_MANY_FILES, + FILERR_INVALID_DATA, + FILERR_INVALID_ACCESS +}; +typedef enum _file_error file_error; + +/* osd_file is an opaque type which represents an open file */ +typedef struct _osd_file osd_file; + + +/*----------------------------------------------------------------------------- + osd_open: open a new file. + + Parameters: + + path - path to the file to open + + openflags - some combination of: + + OPEN_FLAG_READ - open the file for read access + OPEN_FLAG_WRITE - open the file for write access + OPEN_FLAG_CREATE - create/truncate the file when opening + OPEN_FLAG_CREATE_PATHS - specifies that non-existant paths + should be created if necessary + + file - pointer to an osd_file * to receive the newly-opened file + handle; this is only valid if the function returns FILERR_NONE + + filesize - pointer to a UINT64 to receive the size of the opened + file; this is only valid if the function returns FILERR_NONE + + Return value: + + a file_error describing any error that occurred while opening + the file, or FILERR_NONE if no error occurred + + Notes: + + This function is called by mame_fopen and several other places in + the core to access files. These functions will construct paths by + concatenating various search paths held in the options.c options + database with partial paths specified by the core. The core assumes + that the path separator is the first character of the string + PATH_SEPARATOR, but does not interpret any path separators in the + search paths, so if you use a different path separator in a search + path, you may get a mixture of PATH_SEPARATORs (from the core) and + alternate path separators (specified by users and placed into the + options database). +-----------------------------------------------------------------------------*/ +file_error osd_open(const char *path, UINT32 openflags, osd_file **file, UINT64 *filesize); + + +/*----------------------------------------------------------------------------- + osd_close: close an open file + + Parameters: + + file - handle to a file previously opened via osd_open + + Return value: + + a file_error describing any error that occurred while closing + the file, or FILERR_NONE if no error occurred +-----------------------------------------------------------------------------*/ +file_error osd_close(osd_file *file); + + +/*----------------------------------------------------------------------------- + osd_read: read from an open file + + Parameters: + + file - handle to a file previously opened via osd_open + + buffer - pointer to memory that will receive the data read + + offset - offset within the file to read from + + length - number of bytes to read from the file + + actual - pointer to a UINT32 to receive the number of bytes actually + read during the operation; valid only if the function returns + FILERR_NONE + + Return value: + + a file_error describing any error that occurred while reading + from the file, or FILERR_NONE if no error occurred +-----------------------------------------------------------------------------*/ +file_error osd_read(osd_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual); + + +/*----------------------------------------------------------------------------- + osd_write: write to an open file + + Parameters: + + file - handle to a file previously opened via osd_open + + buffer - pointer to memory that contains the data to write + + offset - offset within the file to write to + + length - number of bytes to write to the file + + actual - pointer to a UINT32 to receive the number of bytes actually + written during the operation; valid only if the function returns + FILERR_NONE + + Return value: + + a file_error describing any error that occurred while writing to + the file, or FILERR_NONE if no error occurred +-----------------------------------------------------------------------------*/ +file_error osd_write(osd_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual); + + +/*----------------------------------------------------------------------------- + osd_rmfile: deletes a file + + Parameters: + + filename - path to file to delete + + Return value: + + a file_error describing any error that occurred while deleting + the file, or FILERR_NONE if no error occurred +-----------------------------------------------------------------------------*/ +file_error osd_rmfile(const char *filename); + + +/*----------------------------------------------------------------------------- + osd_get_physical_drive_geometry: if the given path points to a physical + drive, return the geometry of that drive + + Parameters: + + filename - pointer to a path which might describe a physical drive + + cylinders - pointer to a UINT32 to receive the number of cylinders + of the physical drive + + heads - pointer to a UINT32 to receive the number of heads per + cylinder of the physical drive + + sectors - pointer to a UINT32 to receive the number of sectors per + cylinder of the physical drive + + bps - pointer to a UINT32 to receive the number of bytes per sector + of the physical drive + + Return value: + + TRUE if the filename points to a physical drive and if the values + pointed to by cylinders, heads, sectors, and bps are valid; FALSE in + any other case +-----------------------------------------------------------------------------*/ +int osd_get_physical_drive_geometry(const char *filename, UINT32 *cylinders, UINT32 *heads, UINT32 *sectors, UINT32 *bps); + + +/*----------------------------------------------------------------------------- + osd_uchar_from_osdchar: convert the given character or sequence of + characters from the OS-default encoding to a Unicode character + + Parameters: + + uchar - pointer to a UINT32 to receive the resulting unicode + character + + osdchar - pointer to one or more chars that are in the OS-default + encoding + + count - number of characters provided in the OS-default encoding + + Return value: + + The number of characters required to form a Unicode character. +-----------------------------------------------------------------------------*/ +int osd_uchar_from_osdchar(UINT32 /* unicode_char */ *uchar, const char *osdchar, size_t count); + + + +/*************************************************************************** + DIRECTORY INTERFACES +***************************************************************************/ + +/* types of directory entries that can be returned */ +enum _osd_dir_entry_type +{ + ENTTYPE_NONE, + ENTTYPE_FILE, + ENTTYPE_DIR, + ENTTYPE_OTHER +}; +typedef enum _osd_dir_entry_type osd_dir_entry_type; + +/* osd_directory is an opaque type which represents an open directory */ +typedef struct _osd_directory osd_directory; + +/* osd_directory_entry contains basic information about a file when iterating through */ +/* a directory */ +typedef struct _osd_directory_entry osd_directory_entry; +struct _osd_directory_entry +{ + const char * name; /* name of the entry */ + osd_dir_entry_type type; /* type of the entry */ + UINT64 size; /* size of the entry */ +}; + + +/*----------------------------------------------------------------------------- + osd_opendir: open a directory for iteration + + Parameters: + + dirname - path to the directory in question + + Return value: + + upon success, this function should return an osd_directory pointer + which contains opaque data necessary to traverse the directory; on + failure, this function should return NULL +-----------------------------------------------------------------------------*/ +osd_directory *osd_opendir(const char *dirname); + + +/*----------------------------------------------------------------------------- + osd_readdir: return information about the next entry in the directory + + Parameters: + + dir - pointer to an osd_directory that was returned from a prior + call to osd_opendir + + Return value: + + a constant pointer to an osd_directory_entry representing the current item + in the directory, or NULL, indicating that no more entries are + present +-----------------------------------------------------------------------------*/ +const osd_directory_entry *osd_readdir(osd_directory *dir); + + +/*----------------------------------------------------------------------------- + osd_closedir: close an open directory for iteration + + Parameters: + + dir - pointer to an osd_directory that was returned from a prior + call to osd_opendir + + Return value: + + frees any allocated memory and resources associated with the open + directory +-----------------------------------------------------------------------------*/ +void osd_closedir(osd_directory *dir); + + +/*----------------------------------------------------------------------------- + osd_is_absolute_path: returns whether the specified path is absolute + + Parameters: + + path - the path in question + + Return value: + + non-zero if the path is absolute, zero otherwise +-----------------------------------------------------------------------------*/ +int osd_is_absolute_path(const char *path); + + + +/*************************************************************************** + TIMING INTERFACES +***************************************************************************/ + +/* a osd_ticks_t is a 64-bit integer that is used as a core type in timing interfaces */ +typedef INT64 osd_ticks_t; + + +/*----------------------------------------------------------------------------- + osd_ticks: return the current running tick counter + + Parameters: + + None + + Return value: + + an osd_ticks_t value which represents the current tick counter + + Notes: + + The resolution of this counter should be 1ms or better for accurate + performance. It is also important that the source of this timer be + accurate. It is ok if this call is not ultra-fast, since it is + primarily used for once/frame synchronization. +-----------------------------------------------------------------------------*/ +osd_ticks_t osd_ticks(void); + + +/*----------------------------------------------------------------------------- + osd_ticks_per_second: return the number of ticks per second + + Parameters: + + None + + Return value: + + an osd_ticks_t value which represents the number of ticks per + second +-----------------------------------------------------------------------------*/ +osd_ticks_t osd_ticks_per_second(void); + + +/*----------------------------------------------------------------------------- + osd_profiling_ticks: return the current running "profiling" tick counter + + Parameters: + + None + + Return value: + + an osd_ticks_t value which represents the current "profiling" tick + counter + + Notes: + + The profiling tick counter may or may not be different from the + regular tick counter. However, the profiling counter has differing + requirements. First, it must be as fast as possible, so as not to + perturb profiling measurements in a significant way. Second, it + should be a high resolution as possible to provide accurate short- + term measurements (1us resolution or better is ideal). Third, it + is not necessary to calibrate the timing (hence the lack of an + osd_profiling_ticks_per_second call). + + On x86 system, this generally maps to an RDTSC instruction. +-----------------------------------------------------------------------------*/ +osd_ticks_t osd_profiling_ticks(void); + + +/*----------------------------------------------------------------------------- + osd_sleep: sleep for the specified time interval + + Parameters: + + duration - an osd_ticks_t value that specifies how long we should + sleep + + Return value: + + None + + Notes: + + The OSD layer should try to sleep for as close to the specified + duration as possible, or less. This is used as a mechanism to + "give back" unneeded time to other programs running in the system. + On a simple, non multitasking system, this routine can be a no-op. + If there is significant volatility in the amount of time that the + sleep occurs for, the OSD layer should strive to sleep for less time + than specified rather than sleeping too long. +-----------------------------------------------------------------------------*/ +void osd_sleep(osd_ticks_t duration); + + + +/*************************************************************************** + SYNCHRONIZATION INTERFACES +***************************************************************************/ + +/* osd_lock is an opaque type which represents a recursive lock/mutex */ +typedef struct _osd_lock osd_lock; + + +/*----------------------------------------------------------------------------- + osd_lock_alloc: allocate a new lock + + Parameters: + + None. + + Return value: + + A pointer to the allocated lock. +-----------------------------------------------------------------------------*/ +osd_lock *osd_lock_alloc(void); + + +/*----------------------------------------------------------------------------- + osd_lock_acquire: acquire a lock, blocking until it can be acquired + + Parameters: + + lock - a pointer to a previously allocated osd_lock. + + Return value: + + None. + + Notes: + + osd_locks are defined to be recursive. If the current thread already + owns the lock, this function should return immediately. +-----------------------------------------------------------------------------*/ +void osd_lock_acquire(osd_lock *lock); + + +/*----------------------------------------------------------------------------- + osd_lock_try: attempt to acquire a lock + + Parameters: + + lock - a pointer to a previously allocated osd_lock. + + Return value: + + TRUE if the lock was available and was acquired successfully. + FALSE if the lock was already in used by another thread. +-----------------------------------------------------------------------------*/ +int osd_lock_try(osd_lock *lock); + + +/*----------------------------------------------------------------------------- + osd_lock_release: release control of a lock that has been acquired + + Parameters: + + lock - a pointer to a previously allocated osd_lock. + + Return value: + + None. +-----------------------------------------------------------------------------*/ +void osd_lock_release(osd_lock *lock); + + +/*----------------------------------------------------------------------------- + osd_lock_free: free the memory and resources associated with an osd_lock + + Parameters: + + lock - a pointer to a previously allocated osd_lock. + + Return value: + + None. +-----------------------------------------------------------------------------*/ +void osd_lock_free(osd_lock *lock); + + + +/*************************************************************************** + WORK ITEM INTERFACES +***************************************************************************/ + +/* this is the maximum number of supported threads for a single work queue */ +/* threadid values are expected to range from 0..WORK_MAX_THREADS-1 */ +#define WORK_MAX_THREADS 16 + +/* these flags can be set when creating a queue to give hints to the code about + how to configure the queue */ +#define WORK_QUEUE_FLAG_IO 0x0001 +#define WORK_QUEUE_FLAG_MULTI 0x0002 +#define WORK_QUEUE_FLAG_HIGH_FREQ 0x0004 + +/* these flags can be set when queueing a work item to indicate how to handle + its deconstruction */ +#define WORK_ITEM_FLAG_AUTO_RELEASE 0x0001 + +/* osd_work_queue is an opaque type which represents a queue of work items */ +typedef struct _osd_work_queue osd_work_queue; + +/* osd_work_item is an opaque type which represents a single work item */ +typedef struct _osd_work_item osd_work_item; + +/* osd_work_callback is a callback function that does work */ +typedef void *(*osd_work_callback)(void *param, int threadid); + + +/*----------------------------------------------------------------------------- + osd_work_queue_alloc: create a new work queue + + Parameters: + + flags - one or more of the WORK_QUEUE_FLAG_* values ORed together: + + WORK_QUEUE_FLAG_IO - indicates that the work queue will do some + I/O; this may be a useful hint so that threads are created + even on single-processor systems since I/O can often be + overlapped with other work + + WORK_QUEUE_FLAG_MULTI - indicates that the work queue should + take advantage of as many processors as it can; items queued + here are assumed to be fully independent or shared + + WORK_QUEUE_FLAG_HIGH_FREQ - indicates that items are expected + to be queued at high frequency and acted upon quickly; in + general, this implies doing some spin-waiting internally + before falling back to OS-specific synchronization + + Return value: + + A pointer to an allocated osd_work_queue object. + + Notes: + + A work queue abstracts the notion of how potentially threaded work + can be performed. If no threading support is available, it is a + simple matter to execute the work items as they are queued. +-----------------------------------------------------------------------------*/ +osd_work_queue *osd_work_queue_alloc(int flags); + + +/*----------------------------------------------------------------------------- + osd_work_queue_items: return the number of pending items in the queue + + Parameters: + + queue - pointer to an osd_work_queue that was previously created via + osd_work_queue_alloc + + Return value: + + The number of incomplete items remaining in the queue. +-----------------------------------------------------------------------------*/ +int osd_work_queue_items(osd_work_queue *queue); + + +/*----------------------------------------------------------------------------- + osd_work_queue_wait: wait for the queue to be empty + + Parameters: + + queue - pointer to an osd_work_queue that was previously created via + osd_work_queue_alloc + + timeout - a timeout value in osd_ticks_per_second() + + Return value: + + TRUE if the queue is empty; FALSE if the wait timed out before the + queue was emptied. +-----------------------------------------------------------------------------*/ +int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout); + + +/*----------------------------------------------------------------------------- + osd_work_queue_free: free a work queue, waiting for all items to complete + + Parameters: + + queue - pointer to an osd_work_queue that was previously created via + osd_work_queue_alloc + + Return value: + + None. +-----------------------------------------------------------------------------*/ +void osd_work_queue_free(osd_work_queue *queue); + + +/*----------------------------------------------------------------------------- + osd_work_item_queue_multiple: queue a set of work items + + Parameters: + + queue - pointer to an osd_work_queue that was previously created via + osd_work_queue_alloc + + callback - pointer to a function that will do the work + + numitems - number of work items to queue + + param - a void * parameter that can be used to pass data to the + function + + paramstep - the number of bytes to increment param by for each item + queued; for example, if you have an array of work_unit objects, + you can point param to the base of the array and set paramstep to + sizeof(work_unit) + + flags - one or more of the WORK_ITEM_FLAG_* values ORed together: + + WORK_ITEM_FLAG_AUTO_RELEASE - indicates that the work item + should be automatically freed when it is complete + + Return value: + + A pointer to the final allocated osd_work_item in the list. + + Notes: + + On single-threaded systems, this function may actually execute the + work item immediately before returning. +-----------------------------------------------------------------------------*/ +osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags); + + +/* inline helper to queue a single work item using the same interface */ +INLINE osd_work_item *osd_work_item_queue(osd_work_queue *queue, osd_work_callback callback, void *param, UINT32 flags) +{ + return osd_work_item_queue_multiple(queue, callback, 1, param, 0, flags); +} + + +/*----------------------------------------------------------------------------- + osd_work_item_wait: wait for a work item to complete + + Parameters: + + item - pointer to an osd_work_item that was previously returned from + osd_work_item_queue + + timeout - a timeout value in osd_ticks_per_second() + + Return value: + + TRUE if the item completed; FALSE if the wait timed out before the + item completed. +-----------------------------------------------------------------------------*/ +int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout); + + +/*----------------------------------------------------------------------------- + osd_work_item_result: get the result of a work item + + Parameters: + + item - pointer to an osd_work_item that was previously returned from + osd_work_item_queue + + Return value: + + A void * that represents the work item's result. +-----------------------------------------------------------------------------*/ +void *osd_work_item_result(osd_work_item *item); + + +/*----------------------------------------------------------------------------- + osd_work_item_release: release the memory allocated to a work item + + Parameters: + + item - pointer to an osd_work_item that was previously returned from + osd_work_item_queue + + Return value: + + None. + + Notes: + + The osd_work_item exists until explicitly released, even if it has + long since completed. It is the queuer's responsibility to release + any work items it has queued. +-----------------------------------------------------------------------------*/ +void osd_work_item_release(osd_work_item *item); + + + +/*************************************************************************** + MISCELLANEOUS INTERFACES +***************************************************************************/ + +/*----------------------------------------------------------------------------- + osd_alloc_executable: allocate memory that can contain executable code + + Parameters: + + size - the number of bytes to allocate + + Return value: + + a pointer to the allocated memory + + Notes: + + On many systems, this call may acceptably map to malloc(). On systems + where pages are tagged with "no execute" privileges, it may be + necessary to perform some kind of special allocation to ensure that + code placed into this buffer can be executed. +-----------------------------------------------------------------------------*/ +void *osd_alloc_executable(size_t size); + + +/*----------------------------------------------------------------------------- + osd_free_executable: free memory allocated by osd_alloc_executable + + Parameters: + + ptr - the pointer returned from osd_alloc_executable + + size - the number of bytes originally requested + + Return value: + + None +-----------------------------------------------------------------------------*/ +void osd_free_executable(void *ptr, size_t size); + + +/*----------------------------------------------------------------------------- + osd_is_bad_read_ptr: attempt to determine if the given pointer will + generate an access violation if accessed for read + + Parameters: + + ptr - the pointer to examine + + size - the number of bytes to reference + + Return value: + + TRUE if an access to the referenced memory will generate an access + violation on a read; FALSE otherwise. + + Notes: + + This function will eventually be deprecated. +-----------------------------------------------------------------------------*/ +int osd_is_bad_read_ptr(const void *ptr, size_t size); + + +/*----------------------------------------------------------------------------- + osd_break_into_debugger: break into the hosting system's debugger if one + is attached + + Parameters: + + message - pointer to string to output to the debugger + + Return value: + + None. + + Notes: + + This function is called when an assertion or other important error + occurs. If a debugger is attached to the current process, it should + break into the debugger and display the given message. +-----------------------------------------------------------------------------*/ +void osd_break_into_debugger(const char *message); + + +#endif /* __OSDEPEND_H__ */ diff --git a/src/osd/osdepend.h b/src/osd/osdepend.h new file mode 100644 index 00000000000..ddc3fbc0df2 --- /dev/null +++ b/src/osd/osdepend.h @@ -0,0 +1,152 @@ +/*************************************************************************** + + osdepend.h + + OS-dependent code interface. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + The prototypes in this file describe the interfaces that the MAME core + relies upon to interact with the outside world. They are broken out into + several categories. + + The general flow for an OSD port of MAME is as follows: + + - parse the command line or display the frontend + - call run_game (mame.c) with the index in the driver list of + the game selected + - osd_init() is called shortly afterwards; at this time, you are + expected to set up the display system and create render_targets + - the input system will call osd_get_code_list() + - the input port system will call osd_customize_inputport_list() + - the sound system will call osd_start_audio_stream() + - while the game runs, osd_update() will be called periodically + - when the game exits, we return from run_game() + - the OSD layer is now in control again + + This process is expected to be in flux over the next several versions + (this was written during 0.109u2 development) as some of the OSD + responsibilities are pushed into the core. + +*******************************************************************c********/ + +#pragma once + +#ifndef __OSDEPEND_H__ +#define __OSDEPEND_H__ + +#include "mamecore.h" +#include "osdcore.h" +#include "timer.h" + + +/*----------------------------------------------------------------------------- + osd_init: initialize the OSD system. + + Parameters: + + machine - pointer to a structure that contains parameters for the + current "machine" + + Return value: + + None + + Notes: + + This function is responsible for initializing the OSD-specific + video and input functionality, and registering that functionality + with the MAME core. + + In terms of video, this function is expected to create one or more + render_targets that will be used by the MAME core to provide graphics + data to the system. Although it is possible to do this later, the + assumption in the MAME core is that the user interface will be + visible starting at osd_init() time, so you will have some work to + do to avoid these assumptions. + + In terms of input, this function is expected to enumerate all input + devices available and describe them to the MAME core by adding + input devices and their attached items (buttons/axes) via the input + system. + + Beyond these core responsibilities, osd_init() should also initialize + any other OSD systems that require information about the current + running_machine. + + This callback is also the last opportunity to adjust the options + before they are consumed by the rest of the core. + + Note that there is no corresponding osd_exit(). Rather, like most + systems in MAME, you can register an exit callback via the + add_exit_callback() function in mame.c. + + Also note that there is no return value. If you need to report a + fatal error, use the fatalerror() function with a friendly message + to the user. + + Future work/changes: + + Audio initialization may eventually move into here as well, + instead of relying on independent callbacks from each system. +-----------------------------------------------------------------------------*/ +void osd_init(running_machine *machine); + + +void osd_wait_for_debugger(void); + + + +/****************************************************************************** + + Display + +******************************************************************************/ + +void osd_update(int skip_redraw); + + + + +/****************************************************************************** + + Sound + +******************************************************************************/ + +void osd_update_audio_stream(INT16 *buffer, int samples_this_frame); + +/* + control master volume. attenuation is the attenuation in dB (a negative + number). To convert from dB to a linear volume scale do the following: + volume = MAX_VOLUME; + while (attenuation++ < 0) + volume /= 1.122018454; // = (10 ^ (1/20)) = 1dB +*/ +void osd_set_mastervolume(int attenuation); + + + +/****************************************************************************** + + Controls + +******************************************************************************/ + +/* + inptport.c defines some general purpose defaults for key and joystick bindings. + They may be further adjusted by the OS dependent code to better match the + available keyboard, e.g. one could map pause to the Pause key instead of P, or + snapshot to PrtScr instead of F12. Of course the user can further change the + settings to anything he/she likes. + This function is called on startup, before reading the configuration from disk. + Scan the list, and change the keys/joysticks you want. +*/ +void osd_customize_inputport_list(input_port_default_entry *defaults); + + + +#endif /* __OSDEPEND_H__ */ diff --git a/src/osd/osdmini/minidir.c b/src/osd/osdmini/minidir.c new file mode 100644 index 00000000000..b0291cb6c50 --- /dev/null +++ b/src/osd/osdmini/minidir.c @@ -0,0 +1,45 @@ +/*************************************************************************** + + minidir.c - Minimal core directory access functions + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "osdcore.h" + + +//============================================================ +// osd_opendir +//============================================================ + +osd_directory *osd_opendir(const char *dirname) +{ + // since there are no standard C library routines for walking directories, + // we always return an error here + return NULL; +} + + +//============================================================ +// osd_readdir +//============================================================ + +const osd_directory_entry *osd_readdir(osd_directory *dir) +{ + // since there are no standard C library routines for walking directories, + // we always return an error here + return NULL; +} + + +//============================================================ +// osd_closedir +//============================================================ + +void osd_closedir(osd_directory *dir) +{ + // since there are no standard C library routines for walking directories, + // we do nothing +} diff --git a/src/osd/osdmini/minifile.c b/src/osd/osdmini/minifile.c new file mode 100644 index 00000000000..f4e54062d23 --- /dev/null +++ b/src/osd/osdmini/minifile.c @@ -0,0 +1,125 @@ +//============================================================ +// +// minifile.c - Minimal core file access functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#include "osdcore.h" + + +//============================================================ +// osd_open +//============================================================ + +file_error osd_open(const char *path, UINT32 openflags, osd_file **file, UINT64 *filesize) +{ + const char *mode; + FILE *fileptr; + + // based on the flags, choose a mode + if (openflags & OPEN_FLAG_WRITE) + { + if (openflags & OPEN_FLAG_READ) + mode = (openflags & OPEN_FLAG_CREATE) ? "w+b" : "r+b"; + else + mode = "wb"; + } + else if (openflags & OPEN_FLAG_READ) + mode = "rb"; + else + return FILERR_INVALID_ACCESS; + + // open the file + fileptr = fopen(path, mode); + if (fileptr == NULL) + return FILERR_NOT_FOUND; + + // store the file pointer directly as an osd_file + *file = (osd_file *)fileptr; + + // get the size -- note that most fseek/ftell implementations are limited to 32 bits + fseek(fileptr, 0, SEEK_END); + *filesize = ftell(fileptr); + fseek(fileptr, 0, SEEK_SET); + + return FILERR_NONE; +} + + +//============================================================ +// osd_read +//============================================================ + +file_error osd_read(osd_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual) +{ + size_t count; + + // seek to the new location; note that most fseek implementations are limited to 32 bits + fseek((FILE *)file, offset, SEEK_SET); + + // perform the read + count = fread(buffer, 1, length, (FILE *)file); + if (actual != NULL) + *actual = count; + + return FILERR_NONE; +} + + +//============================================================ +// osd_write +//============================================================ + +file_error osd_write(osd_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual) +{ + size_t count; + + // seek to the new location; note that most fseek implementations are limited to 32 bits + fseek((FILE *)file, offset, SEEK_SET); + + // perform the write + count = fwrite(buffer, 1, length, (FILE *)file); + if (actual != NULL) + *actual = count; + + return FILERR_NONE; +} + + +//============================================================ +// osd_close +//============================================================ + +file_error osd_close(osd_file *file) +{ + // close the file handle + fclose((FILE *)file); + return FILERR_NONE; +} + + +//============================================================ +// osd_get_physical_drive_geometry +//============================================================ + +int osd_get_physical_drive_geometry(const char *filename, UINT32 *cylinders, UINT32 *heads, UINT32 *sectors, UINT32 *bps) +{ + // there is no standard way of doing this, so we always return FALSE, indicating + // that a given path is not a physical drive + return FALSE; +} + + +//============================================================ +// osd_uchar_from_osdchar +//============================================================ + +int osd_uchar_from_osdchar(UINT32 /* unicode_char */ *uchar, const char *osdchar, size_t count) +{ + // we assume a standard 1:1 mapping of characters to the first 256 unicode characters + *uchar = (UINT8)*osdchar; + return 1; +} diff --git a/src/osd/osdmini/minimisc.c b/src/osd/osdmini/minimisc.c new file mode 100644 index 00000000000..ca792ed6c76 --- /dev/null +++ b/src/osd/osdmini/minimisc.c @@ -0,0 +1,53 @@ +//============================================================ +// +// minimisc.c - Minimal core miscellaneous functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#include "osdcore.h" + + +//============================================================ +// osd_alloc_executable +//============================================================ + +void *osd_alloc_executable(size_t size) +{ + // to use this version of the code, we have to assume that + // code injected into a malloc'ed region can be safely executed + return malloc(size); +} + + +//============================================================ +// osd_free_executable +//============================================================ + +void osd_free_executable(void *ptr, size_t size) +{ + free(ptr); +} + + +//============================================================ +// osd_is_bad_read_ptr +//============================================================ + +int osd_is_bad_read_ptr(const void *ptr, size_t size) +{ + // there is no standard way to do this, so just say no + return FALSE; +} + + +//============================================================ +// osd_break_into_debugger +//============================================================ + +void osd_break_into_debugger(const char *message) +{ + // there is no standard way to do this, so ignore it +} diff --git a/src/osd/osdmini/minisync.c b/src/osd/osdmini/minisync.c new file mode 100644 index 00000000000..7784c91b1b6 --- /dev/null +++ b/src/osd/osdmini/minisync.c @@ -0,0 +1,67 @@ +//============================================================ +// +// minisync.c - Minimal core synchronization functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#include "osdcore.h" + + +//============================================================ +// osd_lock_alloc +//============================================================ + +osd_lock *osd_lock_alloc(void) +{ + // the minimal implementation does not support threading + // just return a dummy value here + return (osd_lock *)1; +} + + +//============================================================ +// osd_lock_acquire +//============================================================ + +void osd_lock_acquire(osd_lock *lock) +{ + // the minimal implementation does not support threading + // the acquire always "succeeds" +} + + +//============================================================ +// osd_lock_try +//============================================================ + +int osd_lock_try(osd_lock *lock) +{ + // the minimal implementation does not support threading + // the acquire always "succeeds" + return TRUE; +} + + +//============================================================ +// osd_lock_release +//============================================================ + +void osd_lock_release(osd_lock *lock) +{ + // the minimal implementation does not support threading + // do nothing here +} + + +//============================================================ +// osd_lock_free +//============================================================ + +void osd_lock_free(osd_lock *lock) +{ + // the minimal implementation does not support threading + // do nothing here +} diff --git a/src/osd/osdmini/minitime.c b/src/osd/osdmini/minitime.c new file mode 100644 index 00000000000..89d3b4aa4e0 --- /dev/null +++ b/src/osd/osdmini/minitime.c @@ -0,0 +1,57 @@ +//============================================================ +// +// minitime.c - Minimal core timing functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#include "osdepend.h" +#include + + + +//============================================================ +// osd_ticks +//============================================================ + +osd_ticks_t osd_ticks(void) +{ + // use the standard library clock function + return clock(); +} + + +//============================================================ +// osd_ticks_per_second +//============================================================ + +osd_ticks_t osd_ticks_per_second(void) +{ + return CLOCKS_PER_SEC; +} + + +//============================================================ +// osd_profiling_ticks +//============================================================ + +osd_ticks_t osd_profiling_ticks(void) +{ + // on x86 platforms, we should return the value of RDTSC here + // generically, we fall back to clock(), which hopefully is + // fast + return clock(); +} + + +//============================================================ +// osd_sleep +//============================================================ + +void osd_sleep(osd_ticks_t duration) +{ + // if there was a generic, cross-platform way to give up + // time, this is where we would do it +} diff --git a/src/osd/osdmini/miniwork.c b/src/osd/osdmini/miniwork.c new file mode 100644 index 00000000000..3d439e8307d --- /dev/null +++ b/src/osd/osdmini/miniwork.c @@ -0,0 +1,115 @@ +//============================================================ +// +// miniwork.c - Minimal core work item functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#include "osdcore.h" + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +struct _osd_work_item +{ + void *result; +}; + + + +//============================================================ +// osd_work_queue_alloc +//============================================================ + +osd_work_queue *osd_work_queue_alloc(int flags) +{ + // this minimal implementation doesn't need to keep any state + // so we just return a non-NULL pointer + return (osd_work_queue *)1; +} + + +//============================================================ +// osd_work_queue_items +//============================================================ + +int osd_work_queue_items(osd_work_queue *queue) +{ + // we never have pending items + return 0; +} + + +//============================================================ +// osd_work_queue_wait +//============================================================ + +int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout) +{ + // never anything to wait for, so do nothing + return TRUE; +} + + +//============================================================ +// osd_work_queue_free +//============================================================ + +void osd_work_queue_free(osd_work_queue *queue) +{ + // never allocated anything, so nothing to do +} + + +//============================================================ +// osd_work_item_queue +//============================================================ + +osd_work_item *osd_work_item_queue(osd_work_queue *queue, osd_work_callback callback, void *param, UINT32 flags) +{ + osd_work_item *item; + + // allocate memory to hold the result + item = malloc(sizeof(*item)); + if (item == NULL) + return NULL; + + // execute the call directly + item->result = (*callback)(param, 0); + return item; +} + + +//============================================================ +// osd_work_item_wait +//============================================================ + +int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout) +{ + // never anything to wait for, so do nothing + return TRUE; +} + + +//============================================================ +// osd_work_item_result +//============================================================ + +void *osd_work_item_result(osd_work_item *item) +{ + return item->result; +} + + +//============================================================ +// osd_work_item_release +//============================================================ + +void osd_work_item_release(osd_work_item *item) +{ + free(item); +} diff --git a/src/osd/osdmini/osd_cpu.h b/src/osd/osdmini/osd_cpu.h new file mode 100644 index 00000000000..55fb7fd29b1 --- /dev/null +++ b/src/osd/osdmini/osd_cpu.h @@ -0,0 +1,68 @@ +//============================================================ +// +// osd_cpu.h - Minimal core CPU-specific data types +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +/******************************************************************************* +* * +* Define size independent data types and operations. * +* * +* The following types must be supported by all platforms: * +* * +* UINT8 - Unsigned 8-bit Integer INT8 - Signed 8-bit integer * +* UINT16 - Unsigned 16-bit Integer INT16 - Signed 16-bit integer * +* UINT32 - Unsigned 32-bit Integer INT32 - Signed 32-bit integer * +* UINT64 - Unsigned 64-bit Integer INT64 - Signed 64-bit integer * +* * +* * +* The macro names for the artithmatic operations are composed as follows: * +* * +* XXX_R_A_B, where XXX - 3 letter operation code (ADD, SUB, etc.) * +* R - The type of the result * +* A - The type of operand 1 * +* B - The type of operand 2 (if binary operation) * +* * +* Each type is one of: U8,8,U16,16,U32,32,U64,64 * +* * +*******************************************************************************/ + +#pragma once + +#ifndef OSD_CPU_H +#define OSD_CPU_H + +typedef unsigned char UINT8; +typedef signed char INT8; +typedef unsigned short UINT16; +typedef signed short INT16; +typedef unsigned int UINT32; +typedef signed int INT32; +__extension__ typedef unsigned long long UINT64; +__extension__ typedef signed long long INT64; + +/* Combine two 32-bit integers into a 64-bit integer */ +#define COMBINE_64_32_32(A,B) ((((UINT64)(A))<<32) | (UINT32)(B)) +#define COMBINE_U64_U32_U32(A,B) COMBINE_64_32_32(A,B) + +/* Return upper 32 bits of a 64-bit integer */ +#define HI32_32_64(A) (((UINT64)(A)) >> 32) +#define HI32_U32_U64(A) HI32_32_64(A) + +/* Return lower 32 bits of a 64-bit integer */ +#define LO32_32_64(A) ((A) & 0xffffffff) +#define LO32_U32_U64(A) LO32_32_64(A) + +#define DIV_64_64_32(A,B) ((A)/(B)) +#define DIV_U64_U64_U32(A,B) ((A)/(UINT32)(B)) + +#define MOD_32_64_32(A,B) ((A)%(B)) +#define MOD_U32_U64_U32(A,B) ((A)%(UINT32)(B)) + +#define MUL_64_32_32(A,B) ((A)*(INT64)(B)) +#define MUL_U64_U32_U32(A,B) ((A)*(UINT64)(UINT32)(B)) + +#endif /* defined OSD_CPU_H */ diff --git a/src/osd/osdmini/osdmini.mak b/src/osd/osdmini/osdmini.mak new file mode 100644 index 00000000000..dcd4e5ca4ed --- /dev/null +++ b/src/osd/osdmini/osdmini.mak @@ -0,0 +1,23 @@ +########################################################################### +# +# osdmini.mak +# +# Minimal OSD makefile +# +# Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +# Visit http://mamedev.org for licensing and usage restrictions. +# +########################################################################### + + +#------------------------------------------------- +# OSD core library +#------------------------------------------------- + +OSDCOREOBJS = \ + $(OBJ)/$(MAMEOS)/minidir.o \ + $(OBJ)/$(MAMEOS)/minifile.o \ + $(OBJ)/$(MAMEOS)/minimisc.o \ + $(OBJ)/$(MAMEOS)/minisync.o \ + $(OBJ)/$(MAMEOS)/minitime.o \ + $(OBJ)/$(MAMEOS)/miniwork.o \ diff --git a/src/osd/windows/d3d8intf.c b/src/osd/windows/d3d8intf.c new file mode 100644 index 00000000000..4b702b33339 --- /dev/null +++ b/src/osd/windows/d3d8intf.c @@ -0,0 +1,557 @@ +//============================================================ +// +// d3d9intf.c - Direct3D 8.1 abstraction layer +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include + +// MAME headers +#include "mame.h" + +// MAMEOS headers +#include "d3dintf.h" +#include "winmain.h" + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef IDirect3D8 *(WINAPI *direct3dcreate8_ptr)(UINT SDKVersion); + + + +//============================================================ +// PROTOTYPES +//============================================================ + +static void set_interfaces(d3d *d3dptr); + + + +//============================================================ +// INLINES +//============================================================ + +INLINE void convert_present_params(const d3d_present_parameters *params, D3DPRESENT_PARAMETERS *d3d8params) +{ + memset(d3d8params, 0, sizeof(*d3d8params)); + d3d8params->BackBufferWidth = params->BackBufferWidth; + d3d8params->BackBufferHeight = params->BackBufferHeight; + d3d8params->BackBufferFormat = params->BackBufferFormat; + d3d8params->BackBufferCount = params->BackBufferCount; + d3d8params->MultiSampleType = params->MultiSampleType; +// d3d8params->MultiSampleQuality = params->MultiSampleQuality; + d3d8params->SwapEffect = params->SwapEffect; + d3d8params->hDeviceWindow = params->hDeviceWindow; + d3d8params->Windowed = params->Windowed; + d3d8params->EnableAutoDepthStencil = params->EnableAutoDepthStencil; + d3d8params->AutoDepthStencilFormat = params->AutoDepthStencilFormat; + d3d8params->Flags = params->Flags; + d3d8params->FullScreen_RefreshRateInHz = params->FullScreen_RefreshRateInHz; + d3d8params->FullScreen_PresentationInterval = params->PresentationInterval; + if (d3d8params->Windowed) + d3d8params->FullScreen_PresentationInterval = 0; +} + + + +//============================================================ +// drawd3d8_init +//============================================================ + +d3d *drawd3d8_init(void) +{ + direct3dcreate8_ptr direct3dcreate8; + HINSTANCE dllhandle; + IDirect3D8 *d3d8; + d3d *d3dptr; + + // dynamically grab the create function from d3d8.dll + dllhandle = LoadLibrary(TEXT("d3d8.dll")); + if (dllhandle == NULL) + { + mame_printf_verbose("Direct3D: Unable to access d3d8.dll\n"); + return NULL; + } + + // import the create function + direct3dcreate8 = (direct3dcreate8_ptr)GetProcAddress(dllhandle, "Direct3DCreate8"); + if (direct3dcreate8 == NULL) + { + mame_printf_verbose("Direct3D: Unable to find Direct3DCreate8\n"); + FreeLibrary(dllhandle); + dllhandle = NULL; + return NULL; + } + + // create our core direct 3d object + d3d8 = (*direct3dcreate8)(D3D_SDK_VERSION); + if (d3d8 == NULL) + { + mame_printf_verbose("Direct3D: Error attempting to initialize Direct3D8\n"); + FreeLibrary(dllhandle); + dllhandle = NULL; + return NULL; + } + + // allocate an object to hold our data + d3dptr = malloc_or_die(sizeof(*d3dptr)); + d3dptr->version = 8; + d3dptr->d3dobj = d3d8; + d3dptr->dllhandle = dllhandle; + set_interfaces(d3dptr); + + mame_printf_verbose("Direct3D: Using Direct3D 8\n"); + return d3dptr; +} + + + +//============================================================ +// Direct3D interfaces +//============================================================ + +static HRESULT d3d_check_device_format(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT adapterformat, DWORD usage, D3DRESOURCETYPE restype, D3DFORMAT format) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + return IDirect3D8_CheckDeviceFormat(d3d8, adapter, devtype, adapterformat, usage, restype, format); +} + + +static HRESULT d3d_check_device_type(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT format, D3DFORMAT backformat, BOOL windowed) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + return IDirect3D8_CheckDeviceType(d3d8, adapter, devtype, format, backformat, windowed); +} + + +static HRESULT d3d_create_device(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, HWND focus, DWORD behavior, d3d_present_parameters *params, d3d_device **dev) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + D3DPRESENT_PARAMETERS d3d8params; + convert_present_params(params, &d3d8params); + return IDirect3D8_CreateDevice(d3d8, adapter, devtype, focus, behavior, &d3d8params, (IDirect3DDevice8 **)dev); +} + + +static HRESULT d3d_enum_adapter_modes(d3d *d3dptr, UINT adapter, D3DFORMAT format, UINT index, D3DDISPLAYMODE *mode) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + return IDirect3D8_EnumAdapterModes(d3d8, adapter, index, mode); +} + + +static UINT d3d_get_adapter_count(d3d *d3dptr) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + return IDirect3D8_GetAdapterCount(d3d8); +} + + +static HRESULT d3d_get_adapter_display_mode(d3d *d3dptr, UINT adapter, D3DDISPLAYMODE *mode) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + return IDirect3D8_GetAdapterDisplayMode(d3d8, adapter, mode); +} + + +static HRESULT d3d_get_adapter_identifier(d3d *d3dptr, UINT adapter, DWORD flags, d3d_adapter_identifier *identifier) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + D3DADAPTER_IDENTIFIER8 id; + HRESULT result = IDirect3D8_GetAdapterIdentifier(d3d8, adapter, flags, &id); + memcpy(identifier->Driver, id.Driver, sizeof(identifier->Driver)); + memcpy(identifier->Description, id.Description, sizeof(identifier->Description)); + identifier->DriverVersion = id.DriverVersion; + identifier->VendorId = id.VendorId; + identifier->DeviceId = id.DeviceId; + identifier->SubSysId = id.SubSysId; + identifier->Revision = id.Revision; + identifier->DeviceIdentifier = id.DeviceIdentifier; + identifier->WHQLLevel = id.WHQLLevel; + return result; +} + + +static UINT d3d_get_adapter_mode_count(d3d *d3dptr, UINT adapter, D3DFORMAT format) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + return IDirect3D8_GetAdapterModeCount(d3d8, adapter); +} + + +static HMONITOR d3d_get_adapter_monitor(d3d *d3dptr, UINT adapter) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + return IDirect3D8_GetAdapterMonitor(d3d8, adapter); +} + + +static HRESULT d3d_get_caps_dword(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, d3d_caps_index which, DWORD *value) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + D3DCAPS8 caps; + HRESULT result = IDirect3D8_GetDeviceCaps(d3d8, adapter, devtype, &caps); + switch (which) + { + case CAPS_PRESENTATION_INTERVALS: *value = caps.PresentationIntervals; break; + case CAPS_CAPS2: *value = caps.DevCaps; break; + case CAPS_DEV_CAPS: *value = caps.DevCaps; break; + case CAPS_SRCBLEND_CAPS: *value = caps.SrcBlendCaps; break; + case CAPS_DSTBLEND_CAPS: *value = caps.DestBlendCaps; break; + case CAPS_TEXTURE_CAPS: *value = caps.TextureCaps; break; + case CAPS_TEXTURE_FILTER_CAPS: *value = caps.TextureFilterCaps; break; + case CAPS_TEXTURE_ADDRESS_CAPS: *value = caps.TextureAddressCaps; break; + case CAPS_TEXTURE_OP_CAPS: *value = caps.TextureOpCaps; break; + case CAPS_MAX_TEXTURE_ASPECT: *value = caps.MaxTextureAspectRatio; break; + case CAPS_MAX_TEXTURE_WIDTH: *value = caps.MaxTextureWidth; break; + case CAPS_MAX_TEXTURE_HEIGHT: *value = caps.MaxTextureHeight; break; + case CAPS_STRETCH_RECT_FILTER: *value = 0; break; + } + return result; +} + + +static ULONG d3d_release(d3d *d3dptr) +{ + IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj; + ULONG result = IDirect3D8_Release(d3d8); + FreeLibrary(d3dptr->dllhandle); + free(d3dptr); + return result; +} + + +static d3d_interface d3d8_interface = +{ + d3d_check_device_format, + d3d_check_device_type, + d3d_create_device, + d3d_enum_adapter_modes, + d3d_get_adapter_count, + d3d_get_adapter_display_mode, + d3d_get_adapter_identifier, + d3d_get_adapter_mode_count, + d3d_get_adapter_monitor, + d3d_get_caps_dword, + d3d_release +}; + + + +//============================================================ +// Direct3DDevice interfaces +//============================================================ + +static HRESULT device_begin_scene(d3d_device *dev) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_BeginScene(device); +} + + +static HRESULT device_clear(d3d_device *dev, DWORD count, const D3DRECT *rects, DWORD flags, D3DCOLOR color, float z, DWORD stencil) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_Clear(device, count, rects, flags, color, z, stencil); +} + + +static HRESULT device_create_offscreen_plain_surface(d3d_device *dev, UINT width, UINT height, D3DFORMAT format, D3DPOOL pool, d3d_surface **surface) +{ + assert(FALSE); + return D3D_OK; +} + + +static HRESULT device_create_texture(d3d_device *dev, UINT width, UINT height, UINT levels, DWORD usage, D3DFORMAT format, D3DPOOL pool, d3d_texture **texture) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_CreateTexture(device, width, height, levels, usage, format, pool, (IDirect3DTexture8 **)texture); +} + + +static HRESULT device_create_vertex_buffer(d3d_device *dev, UINT length, DWORD usage, DWORD fvf, D3DPOOL pool, d3d_vertex_buffer **buf) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_CreateVertexBuffer(device, length, usage, fvf, pool, (IDirect3DVertexBuffer8 **)buf); +} + + +static HRESULT device_draw_primitive(d3d_device *dev, D3DPRIMITIVETYPE type, UINT start, UINT count) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_DrawPrimitive(device, type, start, count); +} + + +static HRESULT device_end_scene(d3d_device *dev) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_EndScene(device); +} + + +static HRESULT device_get_raster_status(d3d_device *dev, D3DRASTER_STATUS *status) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_GetRasterStatus(device, status); +} + + +static HRESULT device_get_render_target(d3d_device *dev, DWORD index, d3d_surface **surface) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + assert(index == 0); + return IDirect3DDevice8_GetRenderTarget(device, (IDirect3DSurface8 **)surface); +} + + +static HRESULT device_present(d3d_device *dev, const RECT *source, const RECT *dest, HWND override, RGNDATA *dirty, DWORD flags) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_Present(device, source, dest, override, dirty); +} + + +static ULONG device_release(d3d_device *dev) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_Release(device); +} + + +static HRESULT device_reset(d3d_device *dev, d3d_present_parameters *params) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + D3DPRESENT_PARAMETERS d3d8params; + convert_present_params(params, &d3d8params); + return IDirect3DDevice8_Reset(device, &d3d8params); +} + + +static void device_set_gamma_ramp(d3d_device *dev, DWORD flags, const D3DGAMMARAMP *ramp) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + IDirect3DDevice8_SetGammaRamp(device, flags, ramp); +} + + +static HRESULT device_set_render_state(d3d_device *dev, D3DRENDERSTATETYPE state, DWORD value) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_SetRenderState(device, state, value); +} + + +static HRESULT device_set_render_target(d3d_device *dev, DWORD index, d3d_surface *surf) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + IDirect3DSurface8 *surface = (IDirect3DSurface8 *)surf; + assert(index == 0); + return IDirect3DDevice8_SetRenderTarget(device, surface, NULL); +} + + +static HRESULT device_set_stream_source(d3d_device *dev, UINT number, d3d_vertex_buffer *vbuf, UINT stride) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + IDirect3DVertexBuffer8 *vertexbuf = (IDirect3DVertexBuffer8 *)vbuf; + return IDirect3DDevice8_SetStreamSource(device, number, vertexbuf, stride); +} + + +static HRESULT device_set_texture(d3d_device *dev, DWORD stage, d3d_texture *tex) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + IDirect3DBaseTexture8 *texture = (IDirect3DBaseTexture8 *)tex; + return IDirect3DDevice8_SetTexture(device, stage, texture); +} + + +static HRESULT device_set_texture_stage_state(d3d_device *dev, DWORD stage, D3DTEXTURESTAGESTATETYPE state, DWORD value) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_SetTextureStageState(device, stage, state, value); +} + + +static HRESULT device_set_vertex_shader(d3d_device *dev, D3DFORMAT format) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_SetVertexShader(device, format); +} + + +static HRESULT device_stretch_rect(d3d_device *dev, d3d_surface *source, const RECT *srcrect, d3d_surface *dest, const RECT *dstrect, D3DTEXTUREFILTERTYPE filter) +{ + assert(FALSE); + return D3D_OK; +} + + +static HRESULT device_test_cooperative_level(d3d_device *dev) +{ + IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev; + return IDirect3DDevice8_TestCooperativeLevel(device); +} + + +static d3d_device_interface d3d8_device_interface = +{ + device_begin_scene, + device_clear, + device_create_offscreen_plain_surface, + device_create_texture, + device_create_vertex_buffer, + device_draw_primitive, + device_end_scene, + device_get_raster_status, + device_get_render_target, + device_present, + device_release, + device_reset, + device_set_gamma_ramp, + device_set_render_state, + device_set_render_target, + device_set_stream_source, + device_set_texture, + device_set_texture_stage_state, + device_set_vertex_shader, + device_stretch_rect, + device_test_cooperative_level +}; + + + +//============================================================ +// Direct3DSurface interfaces +//============================================================ + +static HRESULT surface_lock_rect(d3d_surface *surf, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags) +{ + IDirect3DSurface8 *surface = (IDirect3DSurface8 *)surf; + return IDirect3DSurface8_LockRect(surface, locked, rect, flags); +} + + +static ULONG surface_release(d3d_surface *surf) +{ + IDirect3DSurface8 *surface = (IDirect3DSurface8 *)surf; + return IDirect3DSurface8_Release(surface); +} + + +static HRESULT surface_unlock_rect(d3d_surface *surf) +{ + IDirect3DSurface8 *surface = (IDirect3DSurface8 *)surf; + return IDirect3DSurface8_UnlockRect(surface); +} + + +static d3d_surface_interface d3d8_surface_interface = +{ + surface_lock_rect, + surface_release, + surface_unlock_rect +}; + + + +//============================================================ +// Direct3DTexture interfaces +//============================================================ + +static HRESULT texture_get_surface_level(d3d_texture *tex, UINT level, d3d_surface **surface) +{ + IDirect3DTexture8 *texture = (IDirect3DTexture8 *)tex; + return IDirect3DTexture8_GetSurfaceLevel(texture, level, (IDirect3DSurface8 **)surface); +} + + +static HRESULT texture_lock_rect(d3d_texture *tex, UINT level, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags) +{ + IDirect3DTexture8 *texture = (IDirect3DTexture8 *)tex; + return IDirect3DTexture8_LockRect(texture, level, locked, rect, flags); +} + + +static ULONG texture_release(d3d_texture *tex) +{ + IDirect3DTexture8 *texture = (IDirect3DTexture8 *)tex; + return IDirect3DTexture8_Release(texture); +} + + +static HRESULT texture_unlock_rect(d3d_texture *tex, UINT level) +{ + IDirect3DTexture8 *texture = (IDirect3DTexture8 *)tex; + return IDirect3DTexture8_UnlockRect(texture, level); +} + + +static d3d_texture_interface d3d8_texture_interface = +{ + texture_get_surface_level, + texture_lock_rect, + texture_release, + texture_unlock_rect +}; + + + +//============================================================ +// Direct3DVertexBuffer interfaces +//============================================================ + +static HRESULT vertex_buffer_lock(d3d_vertex_buffer *vbuf, UINT offset, UINT size, VOID **data, DWORD flags) +{ + IDirect3DVertexBuffer8 *vertexbuf = (IDirect3DVertexBuffer8 *)vbuf; + return IDirect3DVertexBuffer8_Lock(vertexbuf, offset, size, (BYTE **)data, flags); +} + + +static ULONG vertex_buffer_release(d3d_vertex_buffer *vbuf) +{ + IDirect3DVertexBuffer8 *vertexbuf = (IDirect3DVertexBuffer8 *)vbuf; + return IDirect3DVertexBuffer8_Release(vertexbuf); +} + + +static HRESULT vertex_buffer_unlock(d3d_vertex_buffer *vbuf) +{ + IDirect3DVertexBuffer8 *vertexbuf = (IDirect3DVertexBuffer8 *)vbuf; + return IDirect3DVertexBuffer8_Unlock(vertexbuf); +} + + +static d3d_vertex_buffer_interface d3d8_vertex_buffer_interface = +{ + vertex_buffer_lock, + vertex_buffer_release, + vertex_buffer_unlock +}; + + + +//============================================================ +// set_interfaces +//============================================================ + +static void set_interfaces(d3d *d3dptr) +{ + d3dptr->d3d = d3d8_interface; + d3dptr->device = d3d8_device_interface; + d3dptr->surface = d3d8_surface_interface; + d3dptr->texture = d3d8_texture_interface; + d3dptr->vertexbuf = d3d8_vertex_buffer_interface; +} diff --git a/src/osd/windows/d3d9intf.c b/src/osd/windows/d3d9intf.c new file mode 100644 index 00000000000..8884fdeae86 --- /dev/null +++ b/src/osd/windows/d3d9intf.c @@ -0,0 +1,590 @@ +//============================================================ +// +// d3d9intf.c - Direct3D 9 abstraction layer +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include + +// MAME headers +#include "mame.h" + +// MAMEOS headers +#include "d3dintf.h" +#include "winmain.h" + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef IDirect3D9 *(WINAPI *direct3dcreate9_ptr)(UINT SDKVersion); + + + +//============================================================ +// PROTOTYPES +//============================================================ + +static void set_interfaces(d3d *d3dptr); + + + +//============================================================ +// INLINES +//============================================================ + +INLINE void convert_present_params(const d3d_present_parameters *params, D3DPRESENT_PARAMETERS *d3d9params) +{ + memset(d3d9params, 0, sizeof(*d3d9params)); + d3d9params->BackBufferWidth = params->BackBufferWidth; + d3d9params->BackBufferHeight = params->BackBufferHeight; + d3d9params->BackBufferFormat = params->BackBufferFormat; + d3d9params->BackBufferCount = params->BackBufferCount; + d3d9params->MultiSampleType = params->MultiSampleType; + d3d9params->MultiSampleQuality = params->MultiSampleQuality; + d3d9params->SwapEffect = params->SwapEffect; + d3d9params->hDeviceWindow = params->hDeviceWindow; + d3d9params->Windowed = params->Windowed; + d3d9params->EnableAutoDepthStencil = params->EnableAutoDepthStencil; + d3d9params->AutoDepthStencilFormat = params->AutoDepthStencilFormat; + d3d9params->Flags = params->Flags; + d3d9params->FullScreen_RefreshRateInHz = params->FullScreen_RefreshRateInHz; + d3d9params->PresentationInterval = params->PresentationInterval; +} + + + +//============================================================ +// drawd3d9_init +//============================================================ + +d3d *drawd3d9_init(void) +{ + direct3dcreate9_ptr direct3dcreate9; + HINSTANCE dllhandle; + IDirect3D9 *d3d9; + d3d *d3dptr; + + // dynamically grab the create function from d3d9.dll + dllhandle = LoadLibrary(TEXT("d3d9.dll")); + if (dllhandle == NULL) + { + mame_printf_verbose("Direct3D: Unable to access d3d9.dll\n"); + return NULL; + } + + // import the create function + direct3dcreate9 = (direct3dcreate9_ptr)GetProcAddress(dllhandle, "Direct3DCreate9"); + if (direct3dcreate9 == NULL) + { + mame_printf_verbose("Direct3D: Unable to find Direct3DCreate9\n"); + FreeLibrary(dllhandle); + dllhandle = NULL; + return NULL; + } + + // create our core direct 3d object + d3d9 = (*direct3dcreate9)(D3D_SDK_VERSION); + if (d3d9 == NULL) + { + mame_printf_verbose("Direct3D: Error attempting to initialize Direct3D9\n"); + FreeLibrary(dllhandle); + dllhandle = NULL; + return NULL; + } + + // allocate an object to hold our data + d3dptr = malloc_or_die(sizeof(*d3dptr)); + d3dptr->version = 9; + d3dptr->d3dobj = d3d9; + d3dptr->dllhandle = dllhandle; + set_interfaces(d3dptr); + + mame_printf_verbose("Direct3D: Using Direct3D 9\n"); + return d3dptr; +} + + + +//============================================================ +// Direct3D interfaces +//============================================================ + +static HRESULT d3d_check_device_format(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT adapterformat, DWORD usage, D3DRESOURCETYPE restype, D3DFORMAT format) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + return IDirect3D9_CheckDeviceFormat(d3d9, adapter, devtype, adapterformat, usage, restype, format); +} + + +static HRESULT d3d_check_device_type(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT format, D3DFORMAT backformat, BOOL windowed) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + return IDirect3D9_CheckDeviceType(d3d9, adapter, devtype, format, backformat, windowed); +} + + +static HRESULT d3d_create_device(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, HWND focus, DWORD behavior, d3d_present_parameters *params, d3d_device **dev) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + D3DPRESENT_PARAMETERS d3d9params; + convert_present_params(params, &d3d9params); + return IDirect3D9_CreateDevice(d3d9, adapter, devtype, focus, behavior, &d3d9params, (IDirect3DDevice9 **)dev); +} + + +static HRESULT d3d_enum_adapter_modes(d3d *d3dptr, UINT adapter, D3DFORMAT format, UINT index, D3DDISPLAYMODE *mode) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + return IDirect3D9_EnumAdapterModes(d3d9, adapter, format, index, mode); +} + + +static UINT d3d_get_adapter_count(d3d *d3dptr) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + return IDirect3D9_GetAdapterCount(d3d9); +} + + +static HRESULT d3d_get_adapter_display_mode(d3d *d3dptr, UINT adapter, D3DDISPLAYMODE *mode) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + return IDirect3D9_GetAdapterDisplayMode(d3d9, adapter, mode); +} + + +static HRESULT d3d_get_adapter_identifier(d3d *d3dptr, UINT adapter, DWORD flags, d3d_adapter_identifier *identifier) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + D3DADAPTER_IDENTIFIER9 id; + HRESULT result = IDirect3D9_GetAdapterIdentifier(d3d9, adapter, flags, &id); + memcpy(identifier->Driver, id.Driver, sizeof(identifier->Driver)); + memcpy(identifier->Description, id.Description, sizeof(identifier->Description)); + identifier->DriverVersion = id.DriverVersion; + identifier->VendorId = id.VendorId; + identifier->DeviceId = id.DeviceId; + identifier->SubSysId = id.SubSysId; + identifier->Revision = id.Revision; + identifier->DeviceIdentifier = id.DeviceIdentifier; + identifier->WHQLLevel = id.WHQLLevel; + return result; +} + + +static UINT d3d_get_adapter_mode_count(d3d *d3dptr, UINT adapter, D3DFORMAT format) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + return IDirect3D9_GetAdapterModeCount(d3d9, adapter, format); +} + + +static HMONITOR d3d_get_adapter_monitor(d3d *d3dptr, UINT adapter) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + return IDirect3D9_GetAdapterMonitor(d3d9, adapter); +} + + +static HRESULT d3d_get_caps_dword(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, d3d_caps_index which, DWORD *value) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + D3DCAPS9 caps; + HRESULT result = IDirect3D9_GetDeviceCaps(d3d9, adapter, devtype, &caps); + switch (which) + { + case CAPS_PRESENTATION_INTERVALS: *value = caps.PresentationIntervals; break; + case CAPS_CAPS2: *value = caps.Caps2; break; + case CAPS_DEV_CAPS: *value = caps.DevCaps; break; + case CAPS_SRCBLEND_CAPS: *value = caps.SrcBlendCaps; break; + case CAPS_DSTBLEND_CAPS: *value = caps.DestBlendCaps; break; + case CAPS_TEXTURE_CAPS: *value = caps.TextureCaps; break; + case CAPS_TEXTURE_FILTER_CAPS: *value = caps.TextureFilterCaps; break; + case CAPS_TEXTURE_ADDRESS_CAPS: *value = caps.TextureAddressCaps; break; + case CAPS_TEXTURE_OP_CAPS: *value = caps.TextureOpCaps; break; + case CAPS_MAX_TEXTURE_ASPECT: *value = caps.MaxTextureAspectRatio; break; + case CAPS_MAX_TEXTURE_WIDTH: *value = caps.MaxTextureWidth; break; + case CAPS_MAX_TEXTURE_HEIGHT: *value = caps.MaxTextureHeight; break; + case CAPS_STRETCH_RECT_FILTER: *value = caps.StretchRectFilterCaps; break; + } + return result; +} + + +static ULONG d3d_release(d3d *d3dptr) +{ + IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj; + ULONG result = IDirect3D9_Release(d3d9); + FreeLibrary(d3dptr->dllhandle); + free(d3dptr); + return result; +} + + +static d3d_interface d3d9_interface = +{ + d3d_check_device_format, + d3d_check_device_type, + d3d_create_device, + d3d_enum_adapter_modes, + d3d_get_adapter_count, + d3d_get_adapter_display_mode, + d3d_get_adapter_identifier, + d3d_get_adapter_mode_count, + d3d_get_adapter_monitor, + d3d_get_caps_dword, + d3d_release +}; + + + +//============================================================ +// Direct3DDevice interfaces +//============================================================ + +static HRESULT device_begin_scene(d3d_device *dev) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_BeginScene(device); +} + + +static HRESULT device_clear(d3d_device *dev, DWORD count, const D3DRECT *rects, DWORD flags, D3DCOLOR color, float z, DWORD stencil) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_Clear(device, count, rects, flags, color, z, stencil); +} + + +static HRESULT device_create_offscreen_plain_surface(d3d_device *dev, UINT width, UINT height, D3DFORMAT format, D3DPOOL pool, d3d_surface **surface) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_CreateOffscreenPlainSurface(device, width, height, format, pool, (IDirect3DSurface9 **)surface, NULL); +} + + +static HRESULT device_create_texture(d3d_device *dev, UINT width, UINT height, UINT levels, DWORD usage, D3DFORMAT format, D3DPOOL pool, d3d_texture **texture) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_CreateTexture(device, width, height, levels, usage, format, pool, (IDirect3DTexture9 **)texture, NULL); +} + + +static HRESULT device_create_vertex_buffer(d3d_device *dev, UINT length, DWORD usage, DWORD fvf, D3DPOOL pool, d3d_vertex_buffer **buf) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_CreateVertexBuffer(device, length, usage, fvf, pool, (IDirect3DVertexBuffer9 **)buf, NULL); +} + + +static HRESULT device_draw_primitive(d3d_device *dev, D3DPRIMITIVETYPE type, UINT start, UINT count) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_DrawPrimitive(device, type, start, count); +} + + +static HRESULT device_end_scene(d3d_device *dev) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_EndScene(device); +} + + +static HRESULT device_get_raster_status(d3d_device *dev, D3DRASTER_STATUS *status) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_GetRasterStatus(device, 0, status); +} + + +static HRESULT device_get_render_target(d3d_device *dev, DWORD index, d3d_surface **surface) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_GetRenderTarget(device, index, (IDirect3DSurface9 **)surface); +} + + +static HRESULT device_present(d3d_device *dev, const RECT *source, const RECT *dest, HWND override, RGNDATA *dirty, DWORD flags) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + if (flags != 0) + { + IDirect3DSwapChain9 *chain; + HRESULT result = IDirect3DDevice9_GetSwapChain(device, 0, &chain); + if (result == D3D_OK) + { + result = IDirect3DSwapChain9_Present(chain, source, dest, override, dirty, flags); + IDirect3DSwapChain9_Release(chain); + return result; + } + } + return IDirect3DDevice9_Present(device, source, dest, override, dirty); +} + + +static ULONG device_release(d3d_device *dev) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_Release(device); +} + + +static HRESULT device_reset(d3d_device *dev, d3d_present_parameters *params) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + D3DPRESENT_PARAMETERS d3d9params; + convert_present_params(params, &d3d9params); + return IDirect3DDevice9_Reset(device, &d3d9params); +} + + +static void device_set_gamma_ramp(d3d_device *dev, DWORD flags, const D3DGAMMARAMP *ramp) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + IDirect3DDevice9_SetGammaRamp(device, 0, flags, ramp); +} + + +static HRESULT device_set_render_state(d3d_device *dev, D3DRENDERSTATETYPE state, DWORD value) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_SetRenderState(device, state, value); +} + + +static HRESULT device_set_render_target(d3d_device *dev, DWORD index, d3d_surface *surf) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + IDirect3DSurface9 *surface = (IDirect3DSurface9 *)surf; + return IDirect3DDevice9_SetRenderTarget(device, index, surface); +} + + +static HRESULT device_set_stream_source(d3d_device *dev, UINT number, d3d_vertex_buffer *vbuf, UINT stride) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + IDirect3DVertexBuffer9 *vertexbuf = (IDirect3DVertexBuffer9 *)vbuf; + return IDirect3DDevice9_SetStreamSource(device, number, vertexbuf, 0, stride); +} + + +static HRESULT device_set_texture(d3d_device *dev, DWORD stage, d3d_texture *tex) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + IDirect3DBaseTexture9 *texture = (IDirect3DBaseTexture9 *)tex; + return IDirect3DDevice9_SetTexture(device, stage, texture); +} + + +static HRESULT device_set_texture_stage_state(d3d_device *dev, DWORD stage, D3DTEXTURESTAGESTATETYPE state, DWORD value) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + + // some state which was here got pushed into sampler state in D3D9 + switch (state) + { + case D3DTSS_ADDRESSU: + return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_ADDRESSU, value); + case D3DTSS_ADDRESSV: + return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_ADDRESSV, value); + case D3DTSS_BORDERCOLOR: + return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_BORDERCOLOR, value); + case D3DTSS_MAGFILTER: + return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MAGFILTER, value); + case D3DTSS_MINFILTER: + return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MINFILTER, value); + case D3DTSS_MIPFILTER: + return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MIPFILTER, value); + case D3DTSS_MIPMAPLODBIAS: + return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MIPMAPLODBIAS, value); + case D3DTSS_MAXMIPLEVEL: + return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MAXMIPLEVEL, value); + case D3DTSS_MAXANISOTROPY: + return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MAXANISOTROPY, value); + default: + return IDirect3DDevice9_SetTextureStageState(device, stage, state, value); + } +} + + +static HRESULT device_set_vertex_shader(d3d_device *dev, D3DFORMAT format) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_SetFVF(device, format); +} + + +static HRESULT device_stretch_rect(d3d_device *dev, d3d_surface *source, const RECT *srcrect, d3d_surface *dest, const RECT *dstrect, D3DTEXTUREFILTERTYPE filter) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + IDirect3DSurface9 *ssurface = (IDirect3DSurface9 *)source; + IDirect3DSurface9 *dsurface = (IDirect3DSurface9 *)dest; + return IDirect3DDevice9_StretchRect(device, ssurface, srcrect, dsurface, dstrect, filter); +} + + +static HRESULT device_test_cooperative_level(d3d_device *dev) +{ + IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev; + return IDirect3DDevice9_TestCooperativeLevel(device); +} + + +static d3d_device_interface d3d9_device_interface = +{ + device_begin_scene, + device_clear, + device_create_offscreen_plain_surface, + device_create_texture, + device_create_vertex_buffer, + device_draw_primitive, + device_end_scene, + device_get_raster_status, + device_get_render_target, + device_present, + device_release, + device_reset, + device_set_gamma_ramp, + device_set_render_state, + device_set_render_target, + device_set_stream_source, + device_set_texture, + device_set_texture_stage_state, + device_set_vertex_shader, + device_stretch_rect, + device_test_cooperative_level +}; + + + +//============================================================ +// Direct3DSurface interfaces +//============================================================ + +static HRESULT surface_lock_rect(d3d_surface *surf, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags) +{ + IDirect3DSurface9 *surface = (IDirect3DSurface9 *)surf; + return IDirect3DSurface9_LockRect(surface, locked, rect, flags); +} + + +static ULONG surface_release(d3d_surface *surf) +{ + IDirect3DSurface9 *surface = (IDirect3DSurface9 *)surf; + return IDirect3DSurface9_Release(surface); +} + + +static HRESULT surface_unlock_rect(d3d_surface *surf) +{ + IDirect3DSurface9 *surface = (IDirect3DSurface9 *)surf; + return IDirect3DSurface9_UnlockRect(surface); +} + + +static d3d_surface_interface d3d9_surface_interface = +{ + surface_lock_rect, + surface_release, + surface_unlock_rect +}; + + + +//============================================================ +// Direct3DTexture interfaces +//============================================================ + +static HRESULT texture_get_surface_level(d3d_texture *tex, UINT level, d3d_surface **surface) +{ + IDirect3DTexture9 *texture = (IDirect3DTexture9 *)tex; + return IDirect3DTexture9_GetSurfaceLevel(texture, level, (IDirect3DSurface9 **)surface); +} + + +static HRESULT texture_lock_rect(d3d_texture *tex, UINT level, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags) +{ + IDirect3DTexture9 *texture = (IDirect3DTexture9 *)tex; + return IDirect3DTexture9_LockRect(texture, level, locked, rect, flags); +} + + +static ULONG texture_release(d3d_texture *tex) +{ + IDirect3DTexture9 *texture = (IDirect3DTexture9 *)tex; + return IDirect3DTexture9_Release(texture); +} + + +static HRESULT texture_unlock_rect(d3d_texture *tex, UINT level) +{ + IDirect3DTexture9 *texture = (IDirect3DTexture9 *)tex; + return IDirect3DTexture9_UnlockRect(texture, level); +} + + +static d3d_texture_interface d3d9_texture_interface = +{ + texture_get_surface_level, + texture_lock_rect, + texture_release, + texture_unlock_rect +}; + + + +//============================================================ +// Direct3DVertexBuffer interfaces +//============================================================ + +static HRESULT vertex_buffer_lock(d3d_vertex_buffer *vbuf, UINT offset, UINT size, VOID **data, DWORD flags) +{ + IDirect3DVertexBuffer9 *vertexbuf = (IDirect3DVertexBuffer9 *)vbuf; + return IDirect3DVertexBuffer9_Lock(vertexbuf, offset, size, data, flags); +} + + +static ULONG vertex_buffer_release(d3d_vertex_buffer *vbuf) +{ + IDirect3DVertexBuffer9 *vertexbuf = (IDirect3DVertexBuffer9 *)vbuf; + return IDirect3DVertexBuffer9_Release(vertexbuf); +} + + +static HRESULT vertex_buffer_unlock(d3d_vertex_buffer *vbuf) +{ + IDirect3DVertexBuffer9 *vertexbuf = (IDirect3DVertexBuffer9 *)vbuf; + return IDirect3DVertexBuffer9_Unlock(vertexbuf); +} + + +static d3d_vertex_buffer_interface d3d9_vertex_buffer_interface = +{ + vertex_buffer_lock, + vertex_buffer_release, + vertex_buffer_unlock +}; + + + +//============================================================ +// set_interfaces +//============================================================ + +static void set_interfaces(d3d *d3dptr) +{ + d3dptr->d3d = d3d9_interface; + d3dptr->device = d3d9_device_interface; + d3dptr->surface = d3d9_surface_interface; + d3dptr->texture = d3d9_texture_interface; + d3dptr->vertexbuf = d3d9_vertex_buffer_interface; +} diff --git a/src/osd/windows/d3dintf.h b/src/osd/windows/d3dintf.h new file mode 100644 index 00000000000..4c3fee4099f --- /dev/null +++ b/src/osd/windows/d3dintf.h @@ -0,0 +1,241 @@ +//============================================================ +// +// drawd3di.h - Direct3D 8/9 interface abstractions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __WIN_DRAWD3DI__ +#define __WIN_DRAWD3DI__ + + +//============================================================ +// CONSTANTS +//============================================================ + +#ifndef D3DCAPS2_DYNAMICTEXTURES +#define D3DCAPS2_DYNAMICTEXTURES 0x20000000L +#endif + +#ifndef D3DPRESENT_DONOTWAIT +#define D3DPRESENT_DONOTWAIT 0x00000001L +#endif + + +#if (DIRECT3D_VERSION >= 0x0900) +// the following used to be TEXTURESTAGESTATES but are now SAMPLERSTATES +enum +{ + D3DTSS_ADDRESSU = 13, + D3DTSS_ADDRESSV = 14, + D3DTSS_BORDERCOLOR = 15, + D3DTSS_MAGFILTER = 16, + D3DTSS_MINFILTER = 17, + D3DTSS_MIPFILTER = 18, + D3DTSS_MIPMAPLODBIAS = 19, + D3DTSS_MAXMIPLEVEL = 20, + D3DTSS_MAXANISOTROPY = 21 +}; +#endif + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef struct _d3d d3d; +typedef struct _d3d_device d3d_device; +typedef struct _d3d_surface d3d_surface; +typedef struct _d3d_texture d3d_texture; +typedef struct _d3d_vertex_buffer d3d_vertex_buffer; + + +//============================================================ +// Abstracted presentation parameters +//============================================================ + +typedef struct _d3d_present_parameters d3d_present_parameters; +struct _d3d_present_parameters +{ + UINT BackBufferWidth; + UINT BackBufferHeight; + D3DFORMAT BackBufferFormat; + UINT BackBufferCount; + D3DMULTISAMPLE_TYPE MultiSampleType; + DWORD MultiSampleQuality; + D3DSWAPEFFECT SwapEffect; + HWND hDeviceWindow; + BOOL Windowed; + BOOL EnableAutoDepthStencil; + D3DFORMAT AutoDepthStencilFormat; + DWORD Flags; + UINT FullScreen_RefreshRateInHz; + UINT PresentationInterval; +}; + + +//============================================================ +// Abstracted device identifier +//============================================================ + +typedef struct _d3d_adapter_identifier d3d_adapter_identifier; +struct _d3d_adapter_identifier +{ + char Driver[512]; + char Description[512]; + LARGE_INTEGER DriverVersion; + DWORD VendorId; + DWORD DeviceId; + DWORD SubSysId; + DWORD Revision; + GUID DeviceIdentifier; + DWORD WHQLLevel; +}; + + +//============================================================ +// Caps enumeration +//============================================================ + +enum _d3d_caps_index +{ + CAPS_PRESENTATION_INTERVALS, + CAPS_CAPS2, + CAPS_DEV_CAPS, + CAPS_SRCBLEND_CAPS, + CAPS_DSTBLEND_CAPS, + CAPS_TEXTURE_CAPS, + CAPS_TEXTURE_FILTER_CAPS, + CAPS_TEXTURE_ADDRESS_CAPS, + CAPS_TEXTURE_OP_CAPS, + CAPS_MAX_TEXTURE_ASPECT, + CAPS_MAX_TEXTURE_WIDTH, + CAPS_MAX_TEXTURE_HEIGHT, + CAPS_STRETCH_RECT_FILTER +}; +typedef enum _d3d_caps_index d3d_caps_index; + + +//============================================================ +// Direct3D interfaces +//============================================================ + +typedef struct _d3d_interface d3d_interface; +struct _d3d_interface +{ + HRESULT (*check_device_format)(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT adapterformat, DWORD usage, D3DRESOURCETYPE restype, D3DFORMAT format); + HRESULT (*check_device_type)(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT format, D3DFORMAT backformat, BOOL windowed); + HRESULT (*create_device)(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, HWND focus, DWORD behavior, d3d_present_parameters *params, d3d_device **dev); + HRESULT (*enum_adapter_modes)(d3d *d3dptr, UINT adapter, D3DFORMAT format, UINT index, D3DDISPLAYMODE *mode); + UINT (*get_adapter_count)(d3d *d3dptr); + HRESULT (*get_adapter_display_mode)(d3d *d3dptr, UINT adapter, D3DDISPLAYMODE *mode); + HRESULT (*get_adapter_identifier)(d3d *d3dptr, UINT adapter, DWORD flags, d3d_adapter_identifier *identifier); + UINT (*get_adapter_mode_count)(d3d *d3dptr, UINT adapter, D3DFORMAT format); + HMONITOR (*get_adapter_monitor)(d3d *d3dptr, UINT adapter); + HRESULT (*get_caps_dword)(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, d3d_caps_index which, DWORD *value); + ULONG (*release)(d3d *d3dptr); +}; + + +//============================================================ +// Direct3DDevice interfaces +//============================================================ + +typedef struct _d3d_device_interface d3d_device_interface; +struct _d3d_device_interface +{ + HRESULT (*begin_scene)(d3d_device *dev); + HRESULT (*clear)(d3d_device *dev, DWORD count, const D3DRECT *rects, DWORD flags, D3DCOLOR color, float z, DWORD stencil); + HRESULT (*create_offscreen_plain_surface)(d3d_device *dev, UINT width, UINT height, D3DFORMAT format, D3DPOOL pool, d3d_surface **surface); + HRESULT (*create_texture)(d3d_device *dev, UINT width, UINT height, UINT levels, DWORD usage, D3DFORMAT format, D3DPOOL pool, d3d_texture **texture); + HRESULT (*create_vertex_buffer)(d3d_device *dev, UINT length, DWORD usage, DWORD fvf, D3DPOOL pool, d3d_vertex_buffer **buf); + HRESULT (*draw_primitive)(d3d_device *dev, D3DPRIMITIVETYPE type, UINT start, UINT count); + HRESULT (*end_scene)(d3d_device *dev); + HRESULT (*get_raster_status)(d3d_device *dev, D3DRASTER_STATUS *status); + HRESULT (*get_render_target)(d3d_device *dev, DWORD index, d3d_surface **surface); + HRESULT (*present)(d3d_device *dev, const RECT *source, const RECT *dest, HWND override, RGNDATA *dirty, DWORD flags); + ULONG (*release)(d3d_device *dev); + HRESULT (*reset)(d3d_device *dev, d3d_present_parameters *params); + void (*set_gamma_ramp)(d3d_device *dev, DWORD flags, const D3DGAMMARAMP *ramp); + HRESULT (*set_render_state)(d3d_device *dev, D3DRENDERSTATETYPE state, DWORD value); + HRESULT (*set_render_target)(d3d_device *dev, DWORD index, d3d_surface *surf); + HRESULT (*set_stream_source)(d3d_device *dev, UINT number, d3d_vertex_buffer *vbuf, UINT stride); + HRESULT (*set_texture)(d3d_device *dev, DWORD stage, d3d_texture *tex); + HRESULT (*set_texture_stage_state)(d3d_device *dev, DWORD stage, D3DTEXTURESTAGESTATETYPE state, DWORD value); + HRESULT (*set_vertex_shader)(d3d_device *dev, D3DFORMAT format); + HRESULT (*stretch_rect)(d3d_device *dev, d3d_surface *source, const RECT *srcrect, d3d_surface *dest, const RECT *dstrect, D3DTEXTUREFILTERTYPE filter); + HRESULT (*test_cooperative_level)(d3d_device *dev); +}; + + +//============================================================ +// Direct3DSurface interfaces +//============================================================ + +typedef struct _d3d_surface_interface d3d_surface_interface; +struct _d3d_surface_interface +{ + HRESULT (*lock_rect)(d3d_surface *surf, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags); + ULONG (*release)(d3d_surface *tex); + HRESULT (*unlock_rect)(d3d_surface *surf); +}; + + +//============================================================ +// Direct3DTexture interfaces +//============================================================ + +typedef struct _d3d_texture_interface d3d_texture_interface; +struct _d3d_texture_interface +{ + HRESULT (*get_surface_level)(d3d_texture *tex, UINT level, d3d_surface **surface); + HRESULT (*lock_rect)(d3d_texture *tex, UINT level, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags); + ULONG (*release)(d3d_texture *tex); + HRESULT (*unlock_rect)(d3d_texture *tex, UINT level); +}; + + +//============================================================ +// Direct3DVertexBuffer interfaces +//============================================================ + +typedef struct _d3d_vertex_buffer_interface d3d_vertex_buffer_interface; +struct _d3d_vertex_buffer_interface +{ + HRESULT (*lock)(d3d_vertex_buffer *vbuf, UINT offset, UINT size, VOID **data, DWORD flags); + ULONG (*release)(d3d_vertex_buffer *vbuf); + HRESULT (*unlock)(d3d_vertex_buffer *vbuf); +}; + + +//============================================================ +// Core D3D object +//============================================================ + +struct _d3d +{ + // internal objects + int version; + void * d3dobj; + HINSTANCE dllhandle; + + // interface pointers + d3d_interface d3d; + d3d_device_interface device; + d3d_surface_interface surface; + d3d_texture_interface texture; + d3d_vertex_buffer_interface vertexbuf; +}; + + +//============================================================ +// PROTOTYPES +//============================================================ + +d3d *drawd3d8_init(void); +d3d *drawd3d9_init(void); + + +#endif diff --git a/src/osd/windows/debugwin.c b/src/osd/windows/debugwin.c new file mode 100644 index 00000000000..3a1a3c7a9f8 --- /dev/null +++ b/src/osd/windows/debugwin.c @@ -0,0 +1,3025 @@ +//============================================================ +// +// debugwin.c - Win32 debug window handling +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#ifdef _MSC_VER +#include +#endif + +// MAME headers +#include "driver.h" +#include "debug/debugvw.h" +#include "debug/debugcon.h" +#include "debug/debugcpu.h" + +// MAMEOS headers +#include "debugwin.h" +#include "window.h" +#include "video.h" +#include "config.h" +#include "strconv.h" +#include "winutf8.h" + + + +//============================================================ +// PARAMETERS +//============================================================ + +#define MAX_VIEWS 4 +#define EDGE_WIDTH 3 +#define MAX_EDIT_STRING 256 +#define HISTORY_LENGTH 20 +#define MAX_OTHER_WND 4 + +// debugger window styles +#define DEBUG_WINDOW_STYLE (WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN) & (~WS_MINIMIZEBOX & ~WS_MAXIMIZEBOX) +#define DEBUG_WINDOW_STYLE_EX 0 + +// debugger view styles +#define DEBUG_VIEW_STYLE WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN +#define DEBUG_VIEW_STYLE_EX 0 + +// edit box styles +#define EDIT_BOX_STYLE WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL +#define EDIT_BOX_STYLE_EX 0 + +// combo box styles +#define COMBO_BOX_STYLE WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST +#define COMBO_BOX_STYLE_EX 0 + +// horizontal scroll bar styles +#define HSCROLL_STYLE WS_CHILD | WS_VISIBLE | SBS_HORZ +#define HSCROLL_STYLE_EX 0 + +// vertical scroll bar styles +#define VSCROLL_STYLE WS_CHILD | WS_VISIBLE | SBS_VERT +#define VSCROLL_STYLE_EX 0 + + +enum +{ + ID_NEW_MEMORY_WND = 1, + ID_NEW_DISASM_WND, + ID_NEW_LOG_WND, + ID_RUN, + ID_RUN_AND_HIDE, + ID_RUN_VBLANK, + ID_RUN_IRQ, + ID_NEXT_CPU, + ID_STEP, + ID_STEP_OVER, + ID_STEP_OUT, + ID_HARD_RESET, + ID_SOFT_RESET, + ID_EXIT, + + ID_1_BYTE_CHUNKS, + ID_2_BYTE_CHUNKS, + ID_4_BYTE_CHUNKS, + ID_8_BYTE_CHUNKS, + ID_LOGICAL_ADDRESSES, + ID_PHYSICAL_ADDRESSES, + ID_REVERSE_VIEW, + ID_INCREASE_MEM_WIDTH, + ID_DECREASE_MEM_WIDTH, + + ID_SHOW_RAW, + ID_SHOW_ENCRYPTED, + ID_SHOW_COMMENTS, + ID_RUN_TO_CURSOR, + ID_TOGGLE_BREAKPOINT +}; + + + +//============================================================ +// TYPES +//============================================================ + +typedef struct _debugview_info debugview_info; +typedef struct _debugwin_info debugwin_info; + + +struct _debugview_info +{ + debugwin_info * owner; + debug_view * view; + HWND wnd; + HWND hscroll; + HWND vscroll; + UINT8 is_textbuf; +}; + + +struct _debugwin_info +{ + debugwin_info * next; + HWND wnd; + HWND focuswnd; + WNDPROC handler; + + UINT32 minwidth, maxwidth; + UINT32 minheight, maxheight; + void (*recompute_children)(debugwin_info *); + + int (*handle_command)(debugwin_info *, WPARAM, LPARAM); + int (*handle_key)(debugwin_info *, WPARAM, LPARAM); + UINT16 ignore_char_lparam; + + debugview_info view[MAX_VIEWS]; + + HWND editwnd; + char edit_defstr[256]; + void (*process_string)(debugwin_info *, const char *); + WNDPROC original_editproc; + TCHAR history[HISTORY_LENGTH][MAX_EDIT_STRING]; + int history_count; + int last_history; + + HWND otherwnd[MAX_OTHER_WND]; +}; + + +typedef struct _memorycombo_item memorycombo_item; +struct _memorycombo_item +{ + memorycombo_item * next; + char name[256]; + UINT8 cpunum; + UINT8 spacenum; + void * base; + UINT32 length; + UINT8 offset_xor; + UINT8 little_endian; + UINT8 prefsize; +}; + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + + + +//============================================================ +// LOCAL VARIABLES +//============================================================ + +static debugwin_info *window_list; +static debugwin_info *main_console; + +static memorycombo_item *memorycombo; + +static UINT8 debugger_active_countdown; +static UINT8 waiting_for_debugger; + +static HFONT debug_font; +static UINT32 debug_font_height; +static UINT32 debug_font_width; +static UINT32 debug_font_ascent; + +static UINT32 hscroll_height; +static UINT32 vscroll_width; + +static DWORD last_debugger_update; + +static UINT8 temporarily_fake_that_we_are_not_visible; + + + +//============================================================ +// PROTOTYPES +//============================================================ + +static debugwin_info *debug_window_create(LPCSTR title, WNDPROC handler); +static void debug_window_free(debugwin_info *info); +static LRESULT CALLBACK debug_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam); + +static void debug_view_draw_contents(debugview_info *view, HDC dc); +static debugview_info *debug_view_find(debug_view *view); +static LRESULT CALLBACK debug_view_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam); +static void debug_view_update(debug_view *view); +static int debug_view_create(debugwin_info *info, int which, int type); + +static LRESULT CALLBACK debug_edit_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam); + +//static void generic_create_window(int type); +static void generic_recompute_children(debugwin_info *info); + +static void memory_create_window(void); +static void memory_recompute_children(debugwin_info *info); +static void memory_process_string(debugwin_info *info, const char *string); +static void memory_update_checkmarks(debugwin_info *info); +static int memory_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam); +static int memory_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam); + +static void disasm_create_window(void); +static void disasm_recompute_children(debugwin_info *info); +static void disasm_process_string(debugwin_info *info, const char *string); +static void disasm_update_checkmarks(debugwin_info *info); +static int disasm_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam); +static int disasm_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam); +static void disasm_update_caption(HWND wnd); + +static void console_create_window(void); +static void console_recompute_children(debugwin_info *info); +static void console_process_string(debugwin_info *info, const char *string); +static void console_set_cpunum(int cpunum); + +static HMENU create_standard_menubar(void); +static int global_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam); +static int global_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam); +static void smart_set_window_bounds(HWND wnd, HWND parent, RECT *bounds); +static void smart_show_window(HWND wnd, BOOL show); +static void smart_show_all(BOOL show); + + + +//============================================================ +// osd_wait_for_debugger +//============================================================ + +void osd_wait_for_debugger(void) +{ + MSG message; + + // create a console window + if (main_console == NULL) + console_create_window(); + + // update the views in the console to reflect the current CPU + if (main_console != NULL) + console_set_cpunum(cpu_getactivecpu()); + + // make sure the debug windows are visible + waiting_for_debugger = TRUE; + smart_show_all(TRUE); + + // get and process messages + GetMessage(&message, NULL, 0, 0); + last_debugger_update = GetTickCount(); + + switch (message.message) + { + // check for F10 -- we need to capture that ourselves + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + if (message.wParam == VK_F10) + SendMessage(GetFocus(), (message.message == WM_SYSKEYDOWN) ? WM_KEYDOWN : WM_KEYUP, message.wParam, message.lParam); + break; + + // process everything else + default: + winwindow_dispatch_message(&message); + break; + } + + // mark the debugger as active + debugger_active_countdown = 2; + waiting_for_debugger = FALSE; +} + + + +//============================================================ +// debugwin_init_windows +//============================================================ + +void debugwin_init_windows(void) +{ + static int class_registered; + + // register the window classes + if (!class_registered) + { + WNDCLASS wc = { 0 }; + + // initialize the description of the window class + wc.lpszClassName = TEXT("MAMEDebugWindow"); + wc.hInstance = GetModuleHandle(NULL); + wc.lpfnWndProc = debug_window_proc; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.lpszMenuName = NULL; + wc.hbrBackground = NULL; + wc.style = 0; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + + // register the class; fail if we can't + if (!RegisterClass(&wc)) + fatalerror("Unable to register debug window class"); + + // initialize the description of the view class + wc.lpszClassName = TEXT("MAMEDebugView"); + wc.lpfnWndProc = debug_view_proc; + + // register the class; fail if we can't + if (!RegisterClass(&wc)) + fatalerror("Unable to register debug view class"); + + class_registered = TRUE; + } + + // create the font + if (debug_font == NULL) + { + // create a temporary DC + HDC temp_dc = GetDC(NULL); + TEXTMETRIC metrics; + HGDIOBJ old_font; + + if (temp_dc != NULL) + { + // create a standard Lucida Console 8 font + debug_font = CreateFont(-MulDiv(8, GetDeviceCaps(temp_dc, LOGPIXELSY), 72), 0, 0, 0, FW_MEDIUM, FALSE, FALSE, FALSE, + ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, TEXT("Lucida Console")); + if (debug_font == NULL) + fatalerror("Unable to create debug font"); + + // get the metrics + old_font = SelectObject(temp_dc, debug_font); + if (GetTextMetrics(temp_dc, &metrics)) + { + debug_font_width = metrics.tmMaxCharWidth; + debug_font_height = metrics.tmHeight; + debug_font_ascent = metrics.tmAscent + metrics.tmExternalLeading; + } + SelectObject(temp_dc, old_font); + ReleaseDC(NULL, temp_dc); + } + } + + // get other metrics + hscroll_height = GetSystemMetrics(SM_CYHSCROLL); + vscroll_width = GetSystemMetrics(SM_CXVSCROLL); +} + + + +//============================================================ +// debugwin_destroy_windows +//============================================================ + +void debugwin_destroy_windows(void) +{ + // loop over windows and free them + while (window_list) + DestroyWindow(window_list->wnd); + + // free the combobox info + while (memorycombo) + { + void *temp = memorycombo; + memorycombo = memorycombo->next; + free(temp); + } + + main_console = NULL; +} + + + +//============================================================ +// debugwin_show +//============================================================ + +void debugwin_show(int type) +{ + debugwin_info *info; + + // loop over windows and show/hide them + for (info = window_list; info; info = info->next) + ShowWindow(info->wnd, type); +} + + + +//============================================================ +// debugwin_update_during_game +//============================================================ + +void debugwin_update_during_game(void) +{ + int execution_state = debug_get_execution_state(); + + // if we're running live, do some checks + if (execution_state != EXECUTION_STATE_STOPPED) + { + // see if the interrupt key is pressed and break if it is + temporarily_fake_that_we_are_not_visible = TRUE; + if (input_ui_pressed(IPT_UI_DEBUG_BREAK)) + { + debugwin_info *info; + HWND focuswnd = GetFocus(); + + debug_halt_on_next_instruction(); + debug_console_printf("User-initiated break\n"); + + // if we were focused on some window's edit box, reset it to default + for (info = window_list; info; info = info->next) + if (focuswnd == info->editwnd) + { + SendMessage(focuswnd, WM_SETTEXT, (WPARAM)0, (LPARAM)info->edit_defstr); + SendMessage(focuswnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1); + } + } + temporarily_fake_that_we_are_not_visible = FALSE; + } +} + + + +//============================================================ +// debugwin_is_debugger_visible +//============================================================ + +int debugwin_is_debugger_visible(void) +{ + debugwin_info *info; + + // a bit of hackiness to allow us to check key sequences even if we are visible + if (temporarily_fake_that_we_are_not_visible) + return 0; + + // if any one of our windows is visible, return true + for (info = window_list; info; info = info->next) + if (IsWindowVisible(info->wnd)) + return 1; + return 0; +} + + + +//============================================================ +// debug_window_create +//============================================================ + +static debugwin_info *debug_window_create(LPCSTR title, WNDPROC handler) +{ + debugwin_info *info = NULL; + RECT work_bounds; + + // allocate memory + info = malloc_or_die(sizeof(*info)); + memset(info, 0, sizeof(*info)); + + // create the window + info->handler = handler; + info->wnd = win_create_window_ex_utf8(DEBUG_WINDOW_STYLE_EX, "MAMEDebugWindow", title, DEBUG_WINDOW_STYLE, + 0, 0, 100, 100, win_window_list->hwnd, create_standard_menubar(), GetModuleHandle(NULL), info); + if (info->wnd == NULL) + goto cleanup; + + // fill in some defaults + SystemParametersInfo(SPI_GETWORKAREA, 0, &work_bounds, 0); + info->minwidth = 200; + info->minheight = 200; + info->maxwidth = work_bounds.right - work_bounds.left; + info->maxheight = work_bounds.bottom - work_bounds.top; + + // set the default handlers + info->handle_command = global_handle_command; + info->handle_key = global_handle_key; + strcpy(info->edit_defstr, ""); + + // hook us in + info->next = window_list; + window_list = info; + + return info; + +cleanup: + if (info->wnd != NULL) + DestroyWindow(info->wnd); + free(info); + return NULL; +} + + + +//============================================================ +// debug_window_free +//============================================================ + +static void debug_window_free(debugwin_info *info) +{ + debugwin_info *prev, *curr; + int viewnum; + + // first unlink us from the list + for (curr = window_list, prev = NULL; curr; prev = curr, curr = curr->next) + if (curr == info) + { + if (prev) + prev->next = curr->next; + else + window_list = curr->next; + break; + } + + // free any views + for (viewnum = 0; viewnum < MAX_VIEWS; viewnum++) + if (info->view[viewnum].view) + { + debug_view_free(info->view[viewnum].view); + info->view[viewnum].view = NULL; + } + + // free our memory + free(info); +} + + + +//============================================================ +// debug_window_draw_contents +//============================================================ + +static void debug_window_draw_contents(debugwin_info *info, HDC dc) +{ + RECT bounds, parent; + int curview, curwnd; + + // fill the background with light gray + GetClientRect(info->wnd, &parent); + FillRect(dc, &parent, GetStockObject(LTGRAY_BRUSH)); + + // get the parent bounds in screen coords + ClientToScreen(info->wnd, &((POINT *)&parent)[0]); + ClientToScreen(info->wnd, &((POINT *)&parent)[1]); + + // draw edges around all views + for (curview = 0; curview < MAX_VIEWS; curview++) + if (info->view[curview].wnd) + { + GetWindowRect(info->view[curview].wnd, &bounds); + bounds.top -= parent.top; + bounds.bottom -= parent.top; + bounds.left -= parent.left; + bounds.right -= parent.left; + InflateRect(&bounds, EDGE_WIDTH, EDGE_WIDTH); + DrawEdge(dc, &bounds, EDGE_SUNKEN, BF_RECT); + } + + // draw edges around all children + if (info->editwnd) + { + GetWindowRect(info->editwnd, &bounds); + bounds.top -= parent.top; + bounds.bottom -= parent.top; + bounds.left -= parent.left; + bounds.right -= parent.left; + InflateRect(&bounds, EDGE_WIDTH, EDGE_WIDTH); + DrawEdge(dc, &bounds, EDGE_SUNKEN, BF_RECT); + } + + for (curwnd = 0; curwnd < MAX_OTHER_WND; curwnd++) + if (info->otherwnd[curwnd]) + { + GetWindowRect(info->otherwnd[curwnd], &bounds); + bounds.top -= parent.top; + bounds.bottom -= parent.top; + bounds.left -= parent.left; + bounds.right -= parent.left; + InflateRect(&bounds, EDGE_WIDTH, EDGE_WIDTH); + DrawEdge(dc, &bounds, EDGE_SUNKEN, BF_RECT); + } +} + + + +//============================================================ +// debug_window_proc +//============================================================ + +static LRESULT CALLBACK debug_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA); + + // handle a few messages + switch (message) + { + // set the info pointer + case WM_CREATE: + { + CREATESTRUCT *createinfo = (CREATESTRUCT *)lparam; + info = (debugwin_info *)createinfo->lpCreateParams; + SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)createinfo->lpCreateParams); + if (info->handler) + SetWindowLongPtr(wnd, GWLP_WNDPROC, (LONG_PTR)info->handler); + break; + } + + // paint: draw bezels as necessary + case WM_PAINT: + { + PAINTSTRUCT pstruct; + HDC dc = BeginPaint(wnd, &pstruct); + debug_window_draw_contents(info, dc); + EndPaint(wnd, &pstruct); + break; + } + + // keydown: handle debugger keys + case WM_KEYDOWN: + { + if ((*info->handle_key)(info, wparam, lparam)) + info->ignore_char_lparam = lparam >> 16; + break; + } + + // char: ignore chars associated with keys we've handled + case WM_CHAR: + { + if (info->ignore_char_lparam != (lparam >> 16)) + return DefWindowProc(wnd, message, wparam, lparam); + else + info->ignore_char_lparam = 0; + break; + } + + // activate: set the focus + case WM_ACTIVATE: + { + if (wparam != WA_INACTIVE && info->focuswnd != NULL) + SetFocus(info->focuswnd); + break; + } + + // get min/max info: set the minimum window size + case WM_GETMINMAXINFO: + { + MINMAXINFO *minmax = (MINMAXINFO *)lparam; + if (info) + { + minmax->ptMinTrackSize.x = info->minwidth; + minmax->ptMinTrackSize.y = info->minheight; + minmax->ptMaxSize.x = minmax->ptMaxTrackSize.x = info->maxwidth; + minmax->ptMaxSize.y = minmax->ptMaxTrackSize.y = info->maxheight; + } + break; + } + + // sizing: recompute child window locations + case WM_SIZING: + { + if (info->recompute_children) + (*info->recompute_children)(info); + InvalidateRect(wnd, NULL, FALSE); + break; + } + + // mouse wheel: forward to the first view + case WM_MOUSEWHEEL: + { + int delta = (INT16)HIWORD(wparam) / WHEEL_DELTA; + int viewnum = 0; + POINT point; + HWND child; + + // figure out which view we are hovering over + GetCursorPos(&point); + ScreenToClient(info->wnd, &point); + child = ChildWindowFromPoint(info->wnd, point); + if (child) + { + for (viewnum = 0; viewnum < MAX_VIEWS; viewnum++) + if (info->view[viewnum].wnd == child) + break; + if (viewnum == MAX_VIEWS) + break; + } + + // send the appropriate message to this view's scrollbar + if (info->view[viewnum].wnd && info->view[viewnum].vscroll) + { + int message = SB_LINELEFT; + if (delta < 0) + { + message = SB_LINERIGHT; + delta = -delta; + } + while (delta > 0) + { + SendMessage(info->view[viewnum].wnd, WM_VSCROLL, message, (LPARAM)info->view[viewnum].vscroll); + delta--; + } + } + break; + } + + // command: handle a comment + case WM_COMMAND: + if (!(*info->handle_command)(info, wparam, lparam)) + return DefWindowProc(wnd, message, wparam, lparam); + break; + + // close: close the window if it's not the main console + case WM_CLOSE: + if (main_console && main_console->wnd == wnd) + { + smart_show_all(FALSE); + debug_cpu_go(~0); + } + else + DestroyWindow(wnd); + break; + + // destroy: close down the window + case WM_NCDESTROY: + debug_window_free(info); + break; + + // everything else: defaults + default: + return DefWindowProc(wnd, message, wparam, lparam); + } + + return 0; +} + + + +//============================================================ +// debug_view_create +//============================================================ + +static int debug_view_create(debugwin_info *info, int which, int type) +{ + debugview_info *view = &info->view[which]; + void *callback = (void *)debug_view_update; + + // set the owner + view->owner = info; + + // create the child view + view->wnd = CreateWindowEx(DEBUG_VIEW_STYLE_EX, TEXT("MAMEDebugView"), NULL, DEBUG_VIEW_STYLE, + 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), view); + if (view->wnd == NULL) + goto cleanup; + + // create the scroll bars + view->hscroll = CreateWindowEx(HSCROLL_STYLE_EX, TEXT("SCROLLBAR"), NULL, HSCROLL_STYLE, + 0, 0, 100, CW_USEDEFAULT, view->wnd, NULL, GetModuleHandle(NULL), view); + view->vscroll = CreateWindowEx(VSCROLL_STYLE_EX, TEXT("SCROLLBAR"), NULL, VSCROLL_STYLE, + 0, 0, CW_USEDEFAULT, 100, view->wnd, NULL, GetModuleHandle(NULL), view); + if (view->hscroll == NULL || view->vscroll == NULL) + goto cleanup; + + // create the debug view + view->view = debug_view_alloc(type); + if (view->view == NULL) + goto cleanup; + + // set the update handler + debug_view_set_property_fct(view->view, DVP_UPDATE_CALLBACK, callback); + return 1; + +cleanup: + if (view->view) + debug_view_free(view->view); + if (view->hscroll) + DestroyWindow(view->hscroll); + if (view->vscroll) + DestroyWindow(view->vscroll); + if (view->wnd) + DestroyWindow(view->wnd); + return 0; +} + + + +//============================================================ +// debug_view_set_bounds +//============================================================ + +static void debug_view_set_bounds(debugview_info *info, HWND parent, const RECT *newbounds) +{ + RECT bounds = *newbounds; + + // account for the edges and set the bounds + if (info->wnd) + smart_set_window_bounds(info->wnd, parent, &bounds); + + // update + debug_view_update(info->view); +} + + + +//============================================================ +// debug_view_draw_contents +//============================================================ + +static void debug_view_draw_contents(debugview_info *view, HDC windc) +{ + debug_view_char *viewdata; + HGDIOBJ oldfont, oldbitmap; + UINT32 visrows, viscols; + COLORREF oldfgcolor; + UINT32 col, row; + HBITMAP bitmap; + int oldbkmode; + RECT client; + HDC dc; + + // get the client rect + GetClientRect(view->wnd, &client); + + // create a compatible DC and an offscreen bitmap + dc = CreateCompatibleDC(windc); + if (dc == NULL) + return; + bitmap = CreateCompatibleBitmap(windc, client.right, client.bottom); + if (bitmap == NULL) + { + DeleteDC(dc); + return; + } + oldbitmap = SelectObject(dc, bitmap); + + // first get the visible size from the view and a pointer to the data + visrows = debug_view_get_property_UINT32(view->view, DVP_VISIBLE_ROWS); + viscols = debug_view_get_property_UINT32(view->view, DVP_VISIBLE_COLS); + viewdata = debug_view_get_property_ptr(view->view, DVP_VIEW_DATA); + + // set the font + oldfont = SelectObject(dc, debug_font); + oldfgcolor = GetTextColor(dc); + oldbkmode = GetBkMode(dc); + SetBkMode(dc, TRANSPARENT); + + // iterate over rows and columns + for (row = 0; row < visrows; row++) + { + int iter; + + // loop twice; once to fill the background and once to draw the text + for (iter = 0; iter < 2; iter++) + { + COLORREF fgcolor = RGB(0x00,0x00,0x00); + COLORREF bgcolor = RGB(0xff,0xff,0xff); + HBRUSH bgbrush = NULL; + int last_attrib = -1; + TCHAR buffer[256]; + int count = 0; + RECT bounds; + + // initialize the text bounds + bounds.left = bounds.right = 0; + bounds.top = row * debug_font_height; + bounds.bottom = bounds.top + debug_font_height; + + // start with a brush on iteration #0 + if (iter == 0) + bgbrush = CreateSolidBrush(bgcolor); + + // iterate over columns + for (col = 0; col < viscols; col++) + { + // if the attribute changed, adjust the colors + if (viewdata[col].attrib != last_attrib) + { + COLORREF oldbg = bgcolor; + + // reset to standard colors + fgcolor = RGB(0x00,0x00,0x00); + bgcolor = RGB(0xff,0xff,0xff); + + // pick new fg/bg colors + if (viewdata[col].attrib & DCA_ANCILLARY) bgcolor = RGB(0xe0,0xe0,0xe0); + if (viewdata[col].attrib & DCA_SELECTED) bgcolor = RGB(0xff,0x80,0x80); + if (viewdata[col].attrib & DCA_CURRENT) bgcolor = RGB(0xff,0xff,0x00); + if ((viewdata[col].attrib & DCA_SELECTED) && (viewdata[col].attrib & DCA_CURRENT)) bgcolor = RGB(0xff,0xc0,0x80); + if (viewdata[col].attrib & DCA_CHANGED) fgcolor = RGB(0xff,0x00,0x00); + if (viewdata[col].attrib & DCA_INVALID) fgcolor = RGB(0x00,0x00,0xff); + if (viewdata[col].attrib & DCA_DISABLED) fgcolor = RGB((GetRValue(fgcolor) + GetRValue(bgcolor)) / 2, (GetGValue(fgcolor) + GetGValue(bgcolor)) / 2, (GetBValue(fgcolor) + GetBValue(bgcolor)) / 2); + if (viewdata[col].attrib & DCA_COMMENT) fgcolor = RGB(0x00,0x80,0x00); + + // flush any pending drawing + if (count > 0) + { + bounds.right = bounds.left + count * debug_font_width; + if (iter == 0) + FillRect(dc, &bounds, bgbrush); + else + ExtTextOut(dc, bounds.left, bounds.top, 0, NULL, buffer, count, NULL); + bounds.left = bounds.right; + count = 0; + } + + // set the new colors + if (iter == 0 && oldbg != bgcolor) + { + DeleteObject(bgbrush); + bgbrush = CreateSolidBrush(bgcolor); + } + else if (iter == 1) + SetTextColor(dc, fgcolor); + last_attrib = viewdata[col].attrib; + } + + // add this character to the buffer + buffer[count++] = viewdata[col].byte; + } + + // flush any remaining stuff + if (count > 0) + { + bounds.right = bounds.left + count * debug_font_width; + if (iter == 0) + FillRect(dc, &bounds, bgbrush); + else + ExtTextOut(dc, bounds.left, bounds.top, 0, NULL, buffer, count, NULL); + } + + // erase to the end of the line + if (iter == 0) + { + bounds.left = bounds.right; + bounds.right = client.right; + FillRect(dc, &bounds, bgbrush); + DeleteObject(bgbrush); + } + } + + // advance viewdata + viewdata += viscols; + } + + // erase anything beyond the bottom with white + GetClientRect(view->wnd, &client); + client.top = visrows * debug_font_height; + FillRect(dc, &client, (HBRUSH)GetStockObject(WHITE_BRUSH)); + + // reset the font + SetBkMode(dc, oldbkmode); + SetTextColor(dc, oldfgcolor); + SelectObject(dc, oldfont); + + // blit the final results + BitBlt(windc, 0, 0, client.right, client.bottom, dc, 0, 0, SRCCOPY); + + // undo the offscreen stuff + SelectObject(dc, oldbitmap); + DeleteObject(bitmap); + DeleteDC(dc); +} + + + +//============================================================ +// debug_view_update +//============================================================ + +static void debug_view_update(debug_view *view) +{ + debugview_info *info = debug_view_find(view); + + // if we have a view window, process it + if (info && info->view) + { + RECT bounds, vscroll_bounds, hscroll_bounds; + int show_vscroll, show_hscroll; + UINT32 visible_rows, visible_cols; + UINT32 total_rows, total_cols; + UINT32 top_row, left_col; + SCROLLINFO scrollinfo; + + // get the view window bounds + GetClientRect(info->wnd, &bounds); + visible_rows = (bounds.bottom - bounds.top) / debug_font_height; + visible_cols = (bounds.right - bounds.left) / debug_font_width; + + // get the updated total rows/cols and left row/col + total_rows = debug_view_get_property_UINT32(view, DVP_TOTAL_ROWS); + total_cols = debug_view_get_property_UINT32(view, DVP_TOTAL_COLS); + top_row = debug_view_get_property_UINT32(view, DVP_TOP_ROW); + left_col = debug_view_get_property_UINT32(view, DVP_LEFT_COL); + + // determine if we need to show the scrollbars + show_vscroll = show_hscroll = 0; + if (total_rows > visible_rows && bounds.right >= vscroll_width) + { + bounds.right -= vscroll_width; + visible_cols = (bounds.right - bounds.left) / debug_font_width; + show_vscroll = TRUE; + } + if (total_cols > visible_cols && bounds.bottom >= hscroll_height) + { + bounds.bottom -= hscroll_height; + visible_rows = (bounds.bottom - bounds.top) / debug_font_height; + show_hscroll = TRUE; + } + if (!show_vscroll && total_rows > visible_rows && bounds.right >= vscroll_width) + { + bounds.right -= vscroll_width; + visible_cols = (bounds.right - bounds.left) / debug_font_width; + show_vscroll = TRUE; + } + + // compute the bounds of the scrollbars + GetClientRect(info->wnd, &vscroll_bounds); + vscroll_bounds.left = vscroll_bounds.right - vscroll_width; + if (show_hscroll) + vscroll_bounds.bottom -= hscroll_height; + + GetClientRect(info->wnd, &hscroll_bounds); + hscroll_bounds.top = hscroll_bounds.bottom - hscroll_height; + if (show_vscroll) + hscroll_bounds.right -= vscroll_width; + + // if we hid the scrollbars, make sure we reset the top/left corners + if (top_row + visible_rows > total_rows) + top_row = (total_rows > visible_rows) ? (total_rows - visible_rows) : 0; + if (left_col + visible_cols > total_cols) + left_col = (total_cols > visible_cols) ? (total_cols - visible_cols) : 0; + + // fill out the scroll info struct for the vertical scrollbar + scrollinfo.cbSize = sizeof(scrollinfo); + scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; + scrollinfo.nMin = 0; + scrollinfo.nMax = total_rows - 1; + scrollinfo.nPage = visible_rows; + scrollinfo.nPos = top_row; + SetScrollInfo(info->vscroll, SB_CTL, &scrollinfo, TRUE); + + // fill out the scroll info struct for the horizontal scrollbar + scrollinfo.cbSize = sizeof(scrollinfo); + scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; + scrollinfo.nMin = 0; + scrollinfo.nMax = total_cols - 1; + scrollinfo.nPage = visible_cols; + scrollinfo.nPos = left_col; + SetScrollInfo(info->hscroll, SB_CTL, &scrollinfo, TRUE); + + // update window info + visible_rows++; + visible_cols++; + debug_view_set_property_UINT32(view, DVP_VISIBLE_ROWS, visible_rows); + debug_view_set_property_UINT32(view, DVP_VISIBLE_COLS, visible_cols); + debug_view_set_property_UINT32(view, DVP_TOP_ROW, top_row); + debug_view_set_property_UINT32(view, DVP_LEFT_COL, left_col); + + // invalidate the bounds + InvalidateRect(info->wnd, NULL, FALSE); + + // adjust the bounds of the scrollbars and show/hide them + if (info->vscroll) + { + if (show_vscroll) + smart_set_window_bounds(info->vscroll, info->wnd, &vscroll_bounds); + smart_show_window(info->vscroll, show_vscroll); + } + if (info->hscroll) + { + if (show_hscroll) + smart_set_window_bounds(info->hscroll, info->wnd, &hscroll_bounds); + smart_show_window(info->hscroll, show_hscroll); + } + + // if we're in some tight busy loop, handle messages to keep ourselves alive +/* if (GetTickCount() - last_debugger_update > 1000) + { + MSG message; + while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + }*/ + } +} + + + +//============================================================ +// debug_view_find +//============================================================ + +static debugview_info *debug_view_find(debug_view *view) +{ + debugwin_info *info; + int curview; + + // loop over windows and find the view + for (info = window_list; info; info = info->next) + for (curview = 0; curview < MAX_VIEWS; curview++) + if (info->view[curview].view == view) + return &info->view[curview]; + return NULL; +} + + + +//============================================================ +// debug_view_process_scroll +//============================================================ + +static UINT32 debug_view_process_scroll(debugview_info *info, WORD type, HWND wnd) +{ + SCROLLINFO scrollinfo; + INT32 maxval; + INT32 result; + + // get the current info + scrollinfo.cbSize = sizeof(scrollinfo); + scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS; + GetScrollInfo(wnd, SB_CTL, &scrollinfo); + + // by default we stay put + result = scrollinfo.nPos; + + // determine the maximum value + maxval = (scrollinfo.nMax > scrollinfo.nPage) ? (scrollinfo.nMax - scrollinfo.nPage + 1) : 0; + + // handle the message + switch (type) + { + case SB_THUMBTRACK: + result = scrollinfo.nTrackPos; + break; + + case SB_LEFT: + result = 0; + break; + + case SB_RIGHT: + result = maxval; + break; + + case SB_LINELEFT: + result -= 1; + break; + + case SB_LINERIGHT: + result += 1; + break; + + case SB_PAGELEFT: + result -= scrollinfo.nPage - 1; + break; + + case SB_PAGERIGHT: + result += scrollinfo.nPage - 1; + break; + } + + // generic rangecheck + if (result < 0) + result = 0; + if (result > maxval) + result = maxval; + + // set the new position + scrollinfo.fMask = SIF_POS; + scrollinfo.nPos = result; + SetScrollInfo(wnd, SB_CTL, &scrollinfo, TRUE); + + // note if we are at the bottom + if (wnd == info->vscroll && info->is_textbuf && result >= maxval - 1) + return (UINT32)-1; + + return (UINT32)result; +} + + + +//============================================================ +// debug_view_prev_view +//============================================================ + +static void debug_view_prev_view(debugwin_info *info, debugview_info *curview) +{ + int curindex = 1; + int numviews; + + // count the number of views + for (numviews = 0; numviews < MAX_VIEWS; numviews++) + if (info->view[numviews].wnd == NULL) + break; + + // if we have a curview, find out its index + if (curview) + curindex = curview - &info->view[0]; + + // loop until we find someone to take focus + while (1) + { + // advance to the previous index + curindex--; + if (curindex < -1) + curindex = numviews - 1; + + // negative numbers mean the focuswnd + if (curindex < 0 && info->focuswnd != NULL) + { + SetFocus(info->focuswnd); + break; + } + + // positive numbers mean a view + else if (curindex >= 0 && info->view[curindex].wnd != NULL && debug_view_get_property_UINT32(info->view[curindex].view, DVP_SUPPORTS_CURSOR)) + { + SetFocus(info->view[curindex].wnd); + break; + } + } +} + + + +//============================================================ +// debug_view_next_view +//============================================================ + +static void debug_view_next_view(debugwin_info *info, debugview_info *curview) +{ + int curindex = -1; + int numviews; + + // count the number of views + for (numviews = 0; numviews < MAX_VIEWS; numviews++) + if (info->view[numviews].wnd == NULL) + break; + + // if we have a curview, find out its index + if (curview) + curindex = curview - &info->view[0]; + + // loop until we find someone to take focus + while (1) + { + // advance to the previous index + curindex++; + if (curindex >= numviews) + curindex = -1; + + // negative numbers mean the focuswnd + if (curindex < 0 && info->focuswnd != NULL) + { + SetFocus(info->focuswnd); + break; + } + + // positive numbers mean a view + else if (curindex >= 0 && info->view[curindex].wnd != NULL && debug_view_get_property_UINT32(info->view[curindex].view, DVP_SUPPORTS_CURSOR)) + { + SetFocus(info->view[curindex].wnd); + InvalidateRect(info->view[curindex].wnd, NULL, FALSE); + break; + } + } +} + + + +//============================================================ +// debug_view_proc +//============================================================ + +static LRESULT CALLBACK debug_view_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + debugview_info *info = (debugview_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA); + + // handle a few messages + switch (message) + { + // set the info pointer + case WM_CREATE: + { + CREATESTRUCT *createinfo = (CREATESTRUCT *)lparam; + SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)createinfo->lpCreateParams); + break; + } + + // paint: redraw the last bitmap + case WM_PAINT: + { + PAINTSTRUCT pstruct; + HDC dc = BeginPaint(wnd, &pstruct); + debug_view_draw_contents(info, dc); + EndPaint(wnd, &pstruct); + break; + } + + // keydown: handle debugger keys + case WM_KEYDOWN: + { + if ((*info->owner->handle_key)(info->owner, wparam, lparam)) + info->owner->ignore_char_lparam = lparam >> 16; + else + { + switch (wparam) + { + case VK_UP: + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_UP); + info->owner->ignore_char_lparam = lparam >> 16; + break; + + case VK_DOWN: + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_DOWN); + info->owner->ignore_char_lparam = lparam >> 16; + break; + + case VK_LEFT: + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_CTRLLEFT); + else + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_LEFT); + info->owner->ignore_char_lparam = lparam >> 16; + break; + + case VK_RIGHT: + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_CTRLRIGHT); + else + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_RIGHT); + info->owner->ignore_char_lparam = lparam >> 16; + break; + + case VK_PRIOR: + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_PUP); + info->owner->ignore_char_lparam = lparam >> 16; + break; + + case VK_NEXT: + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_PDOWN); + info->owner->ignore_char_lparam = lparam >> 16; + break; + + case VK_HOME: + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_CTRLHOME); + else + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_HOME); + info->owner->ignore_char_lparam = lparam >> 16; + break; + + case VK_END: + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_CTRLEND); + else + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_END); + info->owner->ignore_char_lparam = lparam >> 16; + break; + + case VK_ESCAPE: + if (info->owner->focuswnd != NULL) + SetFocus(info->owner->focuswnd); + info->owner->ignore_char_lparam = lparam >> 16; + break; + + case VK_TAB: + if (GetAsyncKeyState(VK_SHIFT) & 0x8000) + debug_view_prev_view(info->owner, info); + else + debug_view_next_view(info->owner, info); + info->owner->ignore_char_lparam = lparam >> 16; + break; + } + } + break; + } + + // char: ignore chars associated with keys we've handled + case WM_CHAR: + { + if (info->owner->ignore_char_lparam != (lparam >> 16)) + { + if (wparam >= 32 && wparam < 127 && debug_view_get_property_UINT32(info->view, DVP_SUPPORTS_CURSOR)) + debug_view_set_property_UINT32(info->view, DVP_CHARACTER, wparam); + else + return DefWindowProc(wnd, message, wparam, lparam); + } + else + info->owner->ignore_char_lparam = 0; + break; + } + + // gaining focus + case WM_SETFOCUS: + { + if (debug_view_get_property_UINT32(info->view, DVP_SUPPORTS_CURSOR)) + debug_view_set_property_UINT32(info->view, DVP_CURSOR_VISIBLE, 1); + break; + } + + // losing focus + case WM_KILLFOCUS: + { + if (debug_view_get_property_UINT32(info->view, DVP_SUPPORTS_CURSOR)) + debug_view_set_property_UINT32(info->view, DVP_CURSOR_VISIBLE, 0); + break; + } + + // mouse click + case WM_LBUTTONDOWN: + { + if (debug_view_get_property_UINT32(info->view, DVP_SUPPORTS_CURSOR)) + { + int x = GET_X_LPARAM(lparam) / debug_font_width; + int y = GET_Y_LPARAM(lparam) / debug_font_height; + debug_view_begin_update(info->view); + debug_view_set_property_UINT32(info->view, DVP_CURSOR_ROW, debug_view_get_property_UINT32(info->view, DVP_TOP_ROW) + y); + debug_view_set_property_UINT32(info->view, DVP_CURSOR_COL, debug_view_get_property_UINT32(info->view, DVP_LEFT_COL) + x); + debug_view_end_update(info->view); + SetFocus(wnd); + } + break; + } + + // hscroll + case WM_HSCROLL: + { + UINT32 left_col = debug_view_process_scroll(info, LOWORD(wparam), (HWND)lparam); + debug_view_set_property_UINT32(info->view, DVP_LEFT_COL, left_col); + break; + } + + // vscroll + case WM_VSCROLL: + { + UINT32 top_row = debug_view_process_scroll(info, LOWORD(wparam), (HWND)lparam); + if (info->is_textbuf) + debug_view_set_property_UINT32(info->view, DVP_TEXTBUF_LINE_LOCK, top_row); + else + debug_view_set_property_UINT32(info->view, DVP_TOP_ROW, top_row); + break; + } + + // everything else: defaults + default: + return DefWindowProc(wnd, message, wparam, lparam); + } + + return 0; +} + + + +//============================================================ +// debug_edit_proc +//============================================================ + +static LRESULT CALLBACK debug_edit_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA); + TCHAR buffer[MAX_EDIT_STRING]; + + // handle a few messages + switch (message) + { + // key down: handle navigation in the attached view + case WM_KEYDOWN: + switch (wparam) + { + case VK_UP: + if (info->last_history < info->history_count - 1) + info->last_history++; + else + info->last_history = 0; + SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)&info->history[info->last_history][0]); + SendMessage(wnd, EM_SETSEL, (WPARAM)MAX_EDIT_STRING, (LPARAM)MAX_EDIT_STRING); + break; + + case VK_DOWN: + if (info->last_history > 0) + info->last_history--; + else if (info->history_count > 0) + info->last_history = info->history_count - 1; + else + info->last_history = 0; + SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)&info->history[info->last_history][0]); + SendMessage(wnd, EM_SETSEL, (WPARAM)MAX_EDIT_STRING, (LPARAM)MAX_EDIT_STRING); + break; + + case VK_PRIOR: + if (info->view[0].wnd && info->view[0].vscroll) + SendMessage(info->view[0].wnd, WM_VSCROLL, SB_PAGELEFT, (LPARAM)info->view[0].vscroll); + break; + + case VK_NEXT: + if (info->view[0].wnd && info->view[0].vscroll) + SendMessage(info->view[0].wnd, WM_VSCROLL, SB_PAGERIGHT, (LPARAM)info->view[0].vscroll); + break; + + case VK_TAB: + if (GetAsyncKeyState(VK_SHIFT) & 0x8000) + debug_view_prev_view(info, NULL); + else + debug_view_next_view(info, NULL); + info->ignore_char_lparam = lparam >> 16; + break; + + default: + if (!(*info->handle_key)(info, wparam, lparam)) + return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam); + else + info->ignore_char_lparam = lparam >> 16; + break; + } + break; + + // char: handle the return key + case WM_CHAR: + + // ignore chars associated with keys we've handled + if (info->ignore_char_lparam != (lparam >> 16)) + { + switch (wparam) + { + case 13: + { + // fetch the text + SendMessage(wnd, WM_GETTEXT, (WPARAM)ARRAY_LENGTH(buffer), (LPARAM)buffer); + + // add to the history if it's not a repeat of the last one + if (buffer[0] != 0 && _tcscmp(buffer, &info->history[0][0])) + { + memmove(&info->history[1][0], &info->history[0][0], (HISTORY_LENGTH - 1) * MAX_EDIT_STRING); + _tcscpy(&info->history[0][0], buffer); + if (info->history_count < HISTORY_LENGTH) + info->history_count++; + } + info->last_history = info->history_count - 1; + + // process + if (info->process_string) + { + char *utf8_buffer = utf8_from_tstring(buffer); + if (utf8_buffer != NULL) + { + (*info->process_string)(info, utf8_buffer); + free(utf8_buffer); + } + } + break; + } + + case 27: + { + // fetch the text + SendMessage(wnd, WM_GETTEXT, (WPARAM)sizeof(buffer), (LPARAM)buffer); + + // if it's not empty, clear the text + if (_tcslen(buffer) > 0) + { + info->ignore_char_lparam = lparam >> 16; + SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)info->edit_defstr); + } + break; + } + + default: + return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam); + } + } + else + info->ignore_char_lparam = 0; + break; + + // everything else: defaults + default: + return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam); + } + + return 0; +} + + + +//============================================================ +// generic_create_window +//============================================================ + +#ifdef UNUSED_FUNCTION +static void generic_create_window(int type) +{ + debugwin_info *info; + char title[256]; + + // create the window + _snprintf(title, ARRAY_LENGTH(title), "Debug: %s [%s]", Machine->gamedrv->description, Machine->gamedrv->name); + info = debug_window_create(title, NULL); + if (info == NULL || !debug_view_create(info, 0, type)) + return; + + // set the child function + info->recompute_children = generic_recompute_children; + + // recompute the children once to get the maxwidth + generic_recompute_children(info); + + // position the window and recompute children again + SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW); + generic_recompute_children(info); +} +#endif + + + +//============================================================ +// generic_recompute_children +//============================================================ + +static void generic_recompute_children(debugwin_info *info) +{ + RECT parent; + RECT bounds; + UINT32 width; + + // get the view width + width = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS); + + // compute a client rect + bounds.top = bounds.left = 0; + bounds.right = width * debug_font_width + vscroll_width + 2 * EDGE_WIDTH; + bounds.bottom = 200; + AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX); + + // clamp the min/max size + info->maxwidth = bounds.right - bounds.left; + + // get the parent's dimensions + GetClientRect(info->wnd, &parent); + + // view gets the remaining space + InflateRect(&parent, -EDGE_WIDTH, -EDGE_WIDTH); + debug_view_set_bounds(&info->view[0], info->wnd, &parent); +} + + + +//============================================================ +// log_create_window +//============================================================ + +static void log_create_window(void) +{ + debugwin_info *info; + char title[256]; + UINT32 width; + RECT bounds; + + // create the window + _snprintf(title, ARRAY_LENGTH(title), "Errorlog: %s [%s]", Machine->gamedrv->description, Machine->gamedrv->name); + info = debug_window_create(title, NULL); + if (info == NULL || !debug_view_create(info, 0, DVT_LOG)) + return; + info->view->is_textbuf = TRUE; + + // set the child function + info->recompute_children = generic_recompute_children; + + // get the view width + width = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS); + + // compute a client rect + bounds.top = bounds.left = 0; + bounds.right = width * debug_font_width + vscroll_width + 2 * EDGE_WIDTH; + bounds.bottom = 200; + AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX); + + // clamp the min/max size + info->maxwidth = bounds.right - bounds.left; + + // position the window at the bottom-right + SetWindowPos(info->wnd, HWND_TOP, + 100, 100, + bounds.right - bounds.left, bounds.bottom - bounds.top, + SWP_SHOWWINDOW); + + // recompute the children and set focus on the edit box + generic_recompute_children(info); +} + + + +//============================================================ +// memory_determine_combo_items +//============================================================ + +static void memory_determine_combo_items(void) +{ + memorycombo_item **tail = &memorycombo; + UINT32 cpunum, spacenum; + int rgnnum, itemnum; + + // first add all the CPUs' address spaces + for (cpunum = 0; cpunum < MAX_CPU; cpunum++) + { + const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum); + if (cpuinfo->valid) + for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++) + if (cpuinfo->space[spacenum].databytes) + { + memorycombo_item *ci = malloc_or_die(sizeof(*ci)); + memset(ci, 0, sizeof(*ci)); + ci->cpunum = cpunum; + ci->spacenum = spacenum; + ci->prefsize = MIN(cpuinfo->space[spacenum].databytes, 8); + sprintf(ci->name, "CPU #%d (%s) %s memory", cpunum, cpunum_name(cpunum), address_space_names[spacenum]); + *tail = ci; + tail = &ci->next; + } + } + + // then add all the memory regions + for (rgnnum = 0; rgnnum < MAX_MEMORY_REGIONS; rgnnum++) + { + UINT8 *base = memory_region(rgnnum); + UINT32 type = memory_region_type(Machine, rgnnum); + if (base != NULL && type > REGION_INVALID && (type - REGION_INVALID) < ARRAY_LENGTH(memory_region_names)) + { + memorycombo_item *ci = malloc_or_die(sizeof(*ci)); + UINT32 flags = memory_region_flags(Machine, rgnnum); + UINT8 width, little_endian; + memset(ci, 0, sizeof(*ci)); + ci->base = base; + ci->length = memory_region_length(rgnnum); + width = 1 << (flags & ROMREGION_WIDTHMASK); + little_endian = ((flags & ROMREGION_ENDIANMASK) == ROMREGION_LE); + if (type >= REGION_CPU1 && type <= REGION_CPU8) + { + const debug_cpu_info *cpuinfo = debug_get_cpu_info(type - REGION_CPU1); + if (cpuinfo) + { + width = cpuinfo->space[ADDRESS_SPACE_PROGRAM].databytes; + little_endian = (cpuinfo->endianness == CPU_IS_LE); + } + } + ci->prefsize = MIN(width, 8); + ci->offset_xor = width - 1; + ci->little_endian = little_endian; + strcpy(ci->name, memory_region_names[type - REGION_INVALID]); + *tail = ci; + tail = &ci->next; + } + } + + // finally add all global array symbols + for (itemnum = 0; itemnum < 10000; itemnum++) + { + UINT32 valsize, valcount; + const char *name; + void *base; + + /* stop when we run out of items */ + name = state_save_get_indexed_item(itemnum, &base, &valsize, &valcount); + if (name == NULL) + break; + + /* if this is a single-entry global, add it */ + if (valcount > 1 && strstr(name, "/globals/")) + { + memorycombo_item *ci = malloc_or_die(sizeof(*ci)); + memset(ci, 0, sizeof(*ci)); + ci->base = base; + ci->length = valcount * valsize; + ci->prefsize = MIN(valsize, 8); + ci->little_endian = TRUE; + strcpy(ci->name, strrchr(name, '/') + 1); + *tail = ci; + tail = &ci->next; + } + } +} + + +//============================================================ +// memory_update_selection +//============================================================ + +static void memory_update_selection(debugwin_info *info, memorycombo_item *ci) +{ + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_CPUNUM, ci->cpunum); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_SPACENUM, ci->spacenum); + debug_view_set_property_ptr(info->view[0].view, DVP_MEM_RAW_BASE, ci->base); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_RAW_LENGTH, ci->length); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_RAW_OFFSET_XOR, ci->offset_xor); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_RAW_LITTLE_ENDIAN, ci->little_endian); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK, ci->prefsize); + win_set_window_text_utf8(info->wnd, ci->name); +} + + +//============================================================ +// memory_create_window +//============================================================ + +static void memory_create_window(void) +{ + int curcpu = cpu_getactivecpu(), cursel = 0; + memorycombo_item *ci, *selci = NULL; + debugwin_info *info; + HMENU optionsmenu; + + // create the window + info = debug_window_create("Memory", NULL); + if (info == NULL || !debug_view_create(info, 0, DVT_MEMORY)) + return; + + // set the handlers + info->handle_command = memory_handle_command; + info->handle_key = memory_handle_key; + + // create the options menu + optionsmenu = CreatePopupMenu(); + AppendMenu(optionsmenu, MF_ENABLED, ID_1_BYTE_CHUNKS, TEXT("1-byte chunks\tCtrl+1")); + AppendMenu(optionsmenu, MF_ENABLED, ID_2_BYTE_CHUNKS, TEXT("2-byte chunks\tCtrl+2")); + AppendMenu(optionsmenu, MF_ENABLED, ID_4_BYTE_CHUNKS, TEXT("4-byte chunks\tCtrl+4")); + AppendMenu(optionsmenu, MF_ENABLED, ID_8_BYTE_CHUNKS, TEXT("8-byte chunks\tCtrl+8")); + AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); + AppendMenu(optionsmenu, MF_ENABLED, ID_LOGICAL_ADDRESSES, TEXT("Logical Addresses\tCtrl+L")); + AppendMenu(optionsmenu, MF_ENABLED, ID_PHYSICAL_ADDRESSES, TEXT("Physical Addresses\tCtrl+Y")); + AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); + AppendMenu(optionsmenu, MF_ENABLED, ID_REVERSE_VIEW, TEXT("Reverse View\tCtrl+R")); + AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); + AppendMenu(optionsmenu, MF_ENABLED, ID_INCREASE_MEM_WIDTH, TEXT("Increase bytes per line\tCtrl+P")); + AppendMenu(optionsmenu, MF_ENABLED, ID_DECREASE_MEM_WIDTH, TEXT("Decrease bytes per line\tCtrl+O")); + AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options")); + memory_update_checkmarks(info); + + // set up the view to track the initial expression + debug_view_begin_update(info->view[0].view); + debug_view_set_property_string(info->view[0].view, DVP_MEM_EXPRESSION, "0"); + strcpy(info->edit_defstr, "0"); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_TRACK_LIVE, 1); + debug_view_end_update(info->view[0].view); + + // create an edit box and override its key handling + info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE, + 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL); + info->original_editproc = (void *)(FPTR)GetWindowLongPtr(info->editwnd, GWLP_WNDPROC); + SetWindowLongPtr(info->editwnd, GWLP_USERDATA, (LONG_PTR)info); + SetWindowLongPtr(info->editwnd, GWLP_WNDPROC, (LONG_PTR)debug_edit_proc); + SendMessage(info->editwnd, WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE); + SendMessage(info->editwnd, WM_SETTEXT, (WPARAM)0, (LPARAM)"0"); + SendMessage(info->editwnd, EM_LIMITTEXT, (WPARAM)MAX_EDIT_STRING, (LPARAM)0); + SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1); + + // create a combo box + info->otherwnd[0] = CreateWindowEx(COMBO_BOX_STYLE_EX, TEXT("COMBOBOX"), NULL, COMBO_BOX_STYLE, + 0, 0, 100, 1000, info->wnd, NULL, GetModuleHandle(NULL), NULL); + SetWindowLongPtr(info->otherwnd[0], GWLP_USERDATA, (LONG_PTR)info); + SendMessage(info->otherwnd[0], WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE); + + // populate the combobox + if (memorycombo == NULL) + memory_determine_combo_items(); + for (ci = memorycombo; ci; ci = ci->next) + { + int item = SendMessage(info->otherwnd[0], CB_ADDSTRING, 0, (LPARAM)ci->name); + if (ci->base == NULL && ci->cpunum == curcpu && ci->spacenum == ADDRESS_SPACE_PROGRAM) + { + cursel = item; + selci = ci; + } + } + SendMessage(info->otherwnd[0], CB_SETCURSEL, cursel, 0); + + // set the child functions + info->recompute_children = memory_recompute_children; + info->process_string = memory_process_string; + + // set the CPUnum and spacenum properties + if (selci == NULL) + selci = memorycombo; + memory_update_selection(info, selci); + + // recompute the children once to get the maxwidth + memory_recompute_children(info); + + // position the window and recompute children again + SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW); + memory_recompute_children(info); + + // mark the edit box as the default focus and set it + info->focuswnd = info->editwnd; + SetFocus(info->editwnd); +} + + + +//============================================================ +// memory_recompute_children +//============================================================ + +static void memory_recompute_children(debugwin_info *info) +{ + RECT parent, memrect, editrect, comborect; + RECT bounds; + UINT32 width; + + // get the view width + width = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS); + + // compute a client rect + bounds.top = bounds.left = 0; + bounds.right = width * debug_font_width + vscroll_width + 2 * EDGE_WIDTH; + bounds.bottom = 200; + AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX); + + // clamp the min/max size + info->maxwidth = bounds.right - bounds.left; + + // get the parent's dimensions + GetClientRect(info->wnd, &parent); + + // edit box gets half of the width + editrect.top = parent.top + EDGE_WIDTH; + editrect.bottom = editrect.top + debug_font_height + 4; + editrect.left = parent.left + EDGE_WIDTH; + editrect.right = parent.left + (parent.right - parent.left) / 2 - EDGE_WIDTH; + + // combo box gets the other half of the width + comborect.top = editrect.top; + comborect.bottom = editrect.bottom; + comborect.left = editrect.right + 2 * EDGE_WIDTH; + comborect.right = parent.right - EDGE_WIDTH; + + // memory view gets the rest + memrect.top = editrect.bottom + 2 * EDGE_WIDTH; + memrect.bottom = parent.bottom - EDGE_WIDTH; + memrect.left = parent.left + EDGE_WIDTH; + memrect.right = parent.right - EDGE_WIDTH; + + // set the bounds of things + debug_view_set_bounds(&info->view[0], info->wnd, &memrect); + smart_set_window_bounds(info->editwnd, info->wnd, &editrect); + smart_set_window_bounds(info->otherwnd[0], info->wnd, &comborect); +} + + + +//============================================================ +// memory_process_string +//============================================================ + +static void memory_process_string(debugwin_info *info, const char *string) +{ + // set the string to the memory view + debug_view_set_property_string(info->view[0].view, DVP_MEM_EXPRESSION, string); + + // select everything in the edit text box + SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1); + + // update the default string to match + strncpy(info->edit_defstr, string, sizeof(info->edit_defstr) - 1); +} + + + +//============================================================ +// memory_update_checkmarks +//============================================================ + +static void memory_update_checkmarks(debugwin_info *info) +{ + CheckMenuItem(GetMenu(info->wnd), ID_1_BYTE_CHUNKS, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK) == 1? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(GetMenu(info->wnd), ID_2_BYTE_CHUNKS, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK) == 2 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(GetMenu(info->wnd), ID_4_BYTE_CHUNKS, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK) == 4 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(GetMenu(info->wnd), ID_8_BYTE_CHUNKS, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK) == 8 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(GetMenu(info->wnd), ID_LOGICAL_ADDRESSES, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_NO_TRANSLATION) ? MF_UNCHECKED : MF_CHECKED)); + CheckMenuItem(GetMenu(info->wnd), ID_PHYSICAL_ADDRESSES, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_NO_TRANSLATION) ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(GetMenu(info->wnd), ID_REVERSE_VIEW, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_REVERSE_VIEW) ? MF_CHECKED : MF_UNCHECKED)); +} + + + +//============================================================ +// memory_handle_command +//============================================================ + +static int memory_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam) +{ + switch (HIWORD(wparam)) + { + // combo box selection changed + case CBN_SELCHANGE: + { + int sel = SendMessage((HWND)lparam, CB_GETCURSEL, 0, 0); + if (sel != CB_ERR) + { + // find the matching entry + memorycombo_item *ci; + for (ci = memorycombo; ci; ci = ci->next) + if (sel-- == 0) + { + debug_view_begin_update(info->view[0].view); + memory_update_selection(info, ci); + debug_view_end_update(info->view[0].view); + } + + // reset the focus + SetFocus(info->focuswnd); + return 1; + } + break; + } + + // menu selections + case 0: + switch (LOWORD(wparam)) + { + case ID_1_BYTE_CHUNKS: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK, 1); + debug_view_end_update(info->view[0].view); + memory_update_checkmarks(info); + return 1; + + case ID_2_BYTE_CHUNKS: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK, 2); + debug_view_end_update(info->view[0].view); + memory_update_checkmarks(info); + return 1; + + case ID_4_BYTE_CHUNKS: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK, 4); + debug_view_end_update(info->view[0].view); + memory_update_checkmarks(info); + return 1; + + case ID_8_BYTE_CHUNKS: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK, 8); + debug_view_end_update(info->view[0].view); + memory_update_checkmarks(info); + return 1; + + case ID_LOGICAL_ADDRESSES: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_NO_TRANSLATION, FALSE); + debug_view_end_update(info->view[0].view); + memory_update_checkmarks(info); + return 1; + + case ID_PHYSICAL_ADDRESSES: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_NO_TRANSLATION, TRUE); + debug_view_end_update(info->view[0].view); + memory_update_checkmarks(info); + return 1; + + case ID_REVERSE_VIEW: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_REVERSE_VIEW, !debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_REVERSE_VIEW)); + debug_view_end_update(info->view[0].view); + memory_update_checkmarks(info); + return 1; + + case ID_INCREASE_MEM_WIDTH: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_WIDTH, debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_WIDTH) + 1); + debug_view_end_update(info->view[0].view); + return 1; + + case ID_DECREASE_MEM_WIDTH: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_WIDTH, debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_WIDTH) - 1); + debug_view_end_update(info->view[0].view); + return 1; + } + break; + } + return global_handle_command(info, wparam, lparam); +} + + + +//============================================================ +// memory_handle_key +//============================================================ + +static int memory_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam) +{ + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + { + switch (wparam) + { + case '1': + SendMessage(info->wnd, WM_COMMAND, ID_1_BYTE_CHUNKS, 0); + return 1; + + case '2': + SendMessage(info->wnd, WM_COMMAND, ID_2_BYTE_CHUNKS, 0); + return 1; + + case '4': + SendMessage(info->wnd, WM_COMMAND, ID_4_BYTE_CHUNKS, 0); + return 1; + + case '8': + SendMessage(info->wnd, WM_COMMAND, ID_8_BYTE_CHUNKS, 0); + return 1; + + case 'L': + SendMessage(info->wnd, WM_COMMAND, ID_LOGICAL_ADDRESSES, 0); + return 1; + + case 'Y': + SendMessage(info->wnd, WM_COMMAND, ID_PHYSICAL_ADDRESSES, 0); + return 1; + + case 'R': + SendMessage(info->wnd, WM_COMMAND, ID_REVERSE_VIEW, 0); + return 1; + + case 'P': + SendMessage(info->wnd, WM_COMMAND, ID_INCREASE_MEM_WIDTH, 0); + return 1; + + case 'O': + SendMessage(info->wnd, WM_COMMAND, ID_DECREASE_MEM_WIDTH, 0); + return 1; + } + } + return global_handle_key(info, wparam, lparam); +} + + + +//============================================================ +// disasm_create_window +//============================================================ + +static void disasm_create_window(void) +{ + int curcpu = cpu_getactivecpu(), cursel = 0; + debugwin_info *info; + HMENU optionsmenu; + UINT32 cpunum; + + // create the window + info = debug_window_create("Disassembly", NULL); + if (info == NULL || !debug_view_create(info, 0, DVT_DISASSEMBLY)) + return; + + // create the options menu + optionsmenu = CreatePopupMenu(); + AppendMenu(optionsmenu, MF_ENABLED, ID_TOGGLE_BREAKPOINT, TEXT("Set breakpoint at cursor\tF9")); + AppendMenu(optionsmenu, MF_ENABLED, ID_RUN_TO_CURSOR, TEXT("Run to cursor\tF4")); + AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); + AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_RAW, TEXT("Raw opcodes\tCtrl+R")); + AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_ENCRYPTED, TEXT("Encrypted opcodes\tCtrl+E")); + AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_COMMENTS, TEXT("Comments\tCtrl+M")); + AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options")); + disasm_update_checkmarks(info); + + // set the handlers + info->handle_command = disasm_handle_command; + info->handle_key = disasm_handle_key; + + // set up the view to track the initial expression + debug_view_begin_update(info->view[0].view); + debug_view_set_property_string(info->view[0].view, DVP_DASM_EXPRESSION, "curpc"); + strcpy(info->edit_defstr, "curpc"); + debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_TRACK_LIVE, 1); + debug_view_end_update(info->view[0].view); + + // create an edit box and override its key handling + info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE, + 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL); + info->original_editproc = (void *)(FPTR)GetWindowLongPtr(info->editwnd, GWLP_WNDPROC); + SetWindowLongPtr(info->editwnd, GWLP_USERDATA, (LONG_PTR)info); + SetWindowLongPtr(info->editwnd, GWLP_WNDPROC, (LONG_PTR)debug_edit_proc); + SendMessage(info->editwnd, WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE); + SendMessage(info->editwnd, WM_SETTEXT, (WPARAM)0, (LPARAM)"curpc"); + SendMessage(info->editwnd, EM_LIMITTEXT, (WPARAM)MAX_EDIT_STRING, (LPARAM)0); + SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1); + + // create a combo box + info->otherwnd[0] = CreateWindowEx(COMBO_BOX_STYLE_EX, TEXT("COMBOBOX"), NULL, COMBO_BOX_STYLE, + 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL); + SetWindowLongPtr(info->otherwnd[0], GWLP_USERDATA, (LONG_PTR)info); + SendMessage(info->otherwnd[0], WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE); + + // populate the combobox + for (cpunum = 0; cpunum < MAX_CPU; cpunum++) + { + const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum); + if (cpuinfo->valid) + if (cpuinfo->space[ADDRESS_SPACE_PROGRAM].databytes) + { + char name[100]; + int item; + sprintf(name, "CPU #%d (%s)", cpunum, cpunum_name(cpunum)); + item = SendMessage(info->otherwnd[0], CB_ADDSTRING, 0, (LPARAM)name); + if (cpunum == curcpu) + cursel = item; + } + } + SendMessage(info->otherwnd[0], CB_SETCURSEL, cursel, 0); + + // set the child functions + info->recompute_children = disasm_recompute_children; + info->process_string = disasm_process_string; + + // set the CPUnum and spacenum properties + debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM, (curcpu == -1) ? 0 : curcpu); + + // set the caption + disasm_update_caption(info->wnd); + + // recompute the children once to get the maxwidth + disasm_recompute_children(info); + + // position the window and recompute children again + SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW); + disasm_recompute_children(info); + + // mark the edit box as the default focus and set it + info->focuswnd = info->editwnd; + SetFocus(info->editwnd); +} + + + +//============================================================ +// disasm_recompute_children +//============================================================ + +static void disasm_recompute_children(debugwin_info *info) +{ + RECT parent, dasmrect, editrect, comborect; + RECT bounds; + UINT32 width; + + // get the view width + width = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS); + + // compute a client rect + bounds.top = bounds.left = 0; + bounds.right = width * debug_font_width + vscroll_width + 2 * EDGE_WIDTH; + bounds.bottom = 200; + AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX); + + // clamp the min/max size + info->maxwidth = bounds.right - bounds.left; + + // get the parent's dimensions + GetClientRect(info->wnd, &parent); + + // edit box gets half of the width + editrect.top = parent.top + EDGE_WIDTH; + editrect.bottom = editrect.top + debug_font_height + 4; + editrect.left = parent.left + EDGE_WIDTH; + editrect.right = parent.left + (parent.right - parent.left) / 2 - EDGE_WIDTH; + + // combo box gets the other half of the width + comborect.top = editrect.top; + comborect.bottom = editrect.bottom; + comborect.left = editrect.right + 2 * EDGE_WIDTH; + comborect.right = parent.right - EDGE_WIDTH; + + // disasm view gets the rest + dasmrect.top = editrect.bottom + 2 * EDGE_WIDTH; + dasmrect.bottom = parent.bottom - EDGE_WIDTH; + dasmrect.left = parent.left + EDGE_WIDTH; + dasmrect.right = parent.right - EDGE_WIDTH; + + // set the bounds of things + debug_view_set_bounds(&info->view[0], info->wnd, &dasmrect); + smart_set_window_bounds(info->editwnd, info->wnd, &editrect); + smart_set_window_bounds(info->otherwnd[0], info->wnd, &comborect); +} + + + +//============================================================ +// disasm_process_string +//============================================================ + +static void disasm_process_string(debugwin_info *info, const char *string) +{ + // set the string to the disasm view + debug_view_set_property_string(info->view[0].view, DVP_DASM_EXPRESSION, string); + + // select everything in the edit text box + SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1); + + // update the default string to match + strncpy(info->edit_defstr, string, sizeof(info->edit_defstr) - 1); +} + + + +//============================================================ +// disasm_update_checkmarks +//============================================================ + +static void disasm_update_checkmarks(debugwin_info *info) +{ + int rightcol = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_RIGHT_COLUMN); + CheckMenuItem(GetMenu(info->wnd), ID_SHOW_RAW, MF_BYCOMMAND | (rightcol == DVP_DASM_RIGHTCOL_RAW ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(GetMenu(info->wnd), ID_SHOW_ENCRYPTED, MF_BYCOMMAND | (rightcol == DVP_DASM_RIGHTCOL_ENCRYPTED ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(GetMenu(info->wnd), ID_SHOW_COMMENTS, MF_BYCOMMAND | (rightcol == DVP_DASM_RIGHTCOL_COMMENTS ? MF_CHECKED : MF_UNCHECKED)); +} + + + +//============================================================ +// disasm_handle_command +//============================================================ + +static int disasm_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam) +{ + char command[64]; + UINT32 active_address = 0x00; + + switch (HIWORD(wparam)) + { + // combo box selection changed + case CBN_SELCHANGE: + { + int sel = SendMessage((HWND)lparam, CB_GETCURSEL, 0, 0); + if (sel != CB_ERR) + { + // find the matching entry + UINT32 cpunum; + for (cpunum = 0; cpunum < MAX_CPU; cpunum++) + { + const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum); + if (cpuinfo->valid) + if (cpuinfo->space[ADDRESS_SPACE_PROGRAM].databytes) + if (sel-- == 0) + { + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM, cpunum); + debug_view_end_update(info->view[0].view); + disasm_update_caption(info->wnd); + } + } + + // reset the focus + SetFocus(info->focuswnd); + return 1; + } + break; + } + + // menu selections + case 0: + switch (LOWORD(wparam)) + { + case ID_SHOW_RAW: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_RIGHT_COLUMN, DVP_DASM_RIGHTCOL_RAW); + debug_view_end_update(info->view[0].view); + disasm_update_checkmarks(info); + (*info->recompute_children)(info); + return 1; + + case ID_SHOW_ENCRYPTED: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_RIGHT_COLUMN, DVP_DASM_RIGHTCOL_ENCRYPTED); + debug_view_end_update(info->view[0].view); + disasm_update_checkmarks(info); + (*info->recompute_children)(info); + return 1; + + case ID_SHOW_COMMENTS: + debug_view_begin_update(info->view[0].view); + debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_RIGHT_COLUMN, DVP_DASM_RIGHTCOL_COMMENTS); + debug_view_end_update(info->view[0].view); + disasm_update_checkmarks(info); + (*info->recompute_children)(info); + return 1; + + case ID_RUN_TO_CURSOR: + if (debug_view_get_property_UINT32(info->view[0].view, DVP_CURSOR_VISIBLE)) + { + UINT32 cpu_num = 0; + debug_cpu_info *cpuinfo ; + + /* for BYTE2ADDR */ + cpu_num = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM); + cpuinfo = (debug_cpu_info *)debug_get_cpu_info(cpu_num); + + active_address = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_ACTIVE_ADDRESS); + sprintf(command, "go %X", BYTE2ADDR(active_address, cpuinfo, ADDRESS_SPACE_PROGRAM)); + debug_console_execute_command(command, 1); + } + return 1; + + case ID_TOGGLE_BREAKPOINT: + if (debug_view_get_property_UINT32(info->view[0].view, DVP_CURSOR_VISIBLE)) + { + UINT32 cpu_num = 0; + debug_cpu_info *cpuinfo ; + debug_cpu_breakpoint *bp; + INT8 bp_exists = 0; + UINT32 bp_num = 0; + + /* what address are we dealing with? */ + active_address = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_ACTIVE_ADDRESS); + + /* is there already a breakpoint there? */ + cpu_num = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM); + cpuinfo = (debug_cpu_info*)debug_get_cpu_info(cpu_num); + + for (bp = cpuinfo->first_bp; bp; bp = bp->next) + { + if (BYTE2ADDR(active_address, cpuinfo, ADDRESS_SPACE_PROGRAM) == bp->address) + { + bp_exists = 1; + bp_num = bp->index; + } + } + + /* Toggle */ + if (!bp_exists) + sprintf(command, "bpset %X", BYTE2ADDR(active_address, cpuinfo, ADDRESS_SPACE_PROGRAM)); + else + sprintf(command, "bpclear %X", bp_num); + debug_console_execute_command(command, 1); + } + return 1; + } + break; + } + return global_handle_command(info, wparam, lparam); +} + + + +//============================================================ +// disasm_handle_key +//============================================================ + +static int disasm_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam) +{ + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + { + switch (wparam) + { + case 'R': + SendMessage(info->wnd, WM_COMMAND, ID_SHOW_RAW, 0); + return 1; + + case 'E': + SendMessage(info->wnd, WM_COMMAND, ID_SHOW_ENCRYPTED, 0); + return 1; + + case 'N': + SendMessage(info->wnd, WM_COMMAND, ID_SHOW_COMMENTS, 0); + return 1; + } + } + + switch (wparam) + { + /* ajg - steals the F4 from the global key handler - but ALT+F4 didn't work anyways ;) */ + case VK_F4: + SendMessage(info->wnd, WM_COMMAND, ID_RUN_TO_CURSOR, 0); + return 1; + + case VK_F9: + SendMessage(info->wnd, WM_COMMAND, ID_TOGGLE_BREAKPOINT, 0); + return 1; + + case VK_RETURN: + if (debug_view_get_property_UINT32(info->view[0].view, DVP_CURSOR_VISIBLE)) + { + SendMessage(info->wnd, WM_COMMAND, ID_STEP, 0); + return 1; + } + break; + } + + return global_handle_key(info, wparam, lparam); +} + + + +//============================================================ +// disasm_update_caption +//============================================================ + +static void disasm_update_caption(HWND wnd) +{ + debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA); + char title[100]; + UINT32 cpunum; + + // get the properties + cpunum = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM); + + // then update the caption + sprintf(title, "Disassembly: %s (%d)", cpunum_name(cpunum), cpunum); + win_set_window_text_utf8(wnd, title); +} + + + +//============================================================ +// console_create_window +//============================================================ + +void console_create_window(void) +{ + debugwin_info *info; + int bestwidth, bestheight; + RECT bounds, work_bounds; + HMENU optionsmenu; + UINT32 cpunum; + + // create the window + info = debug_window_create("Debug", NULL); + if (info == NULL) + return; + main_console = info; + console_set_cpunum(0); + + // create the views + if (!debug_view_create(info, 0, DVT_DISASSEMBLY)) + goto cleanup; + if (!debug_view_create(info, 1, DVT_REGISTERS)) + goto cleanup; + if (!debug_view_create(info, 2, DVT_CONSOLE)) + goto cleanup; + + // create the options menu + optionsmenu = CreatePopupMenu(); + AppendMenu(optionsmenu, MF_ENABLED, ID_TOGGLE_BREAKPOINT, TEXT("Set breakpoint at cursor\tF9")); + AppendMenu(optionsmenu, MF_ENABLED, ID_RUN_TO_CURSOR, TEXT("Run to cursor\tF4")); + AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); + AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_RAW, TEXT("Raw opcodes\tCtrl+R")); + AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_ENCRYPTED, TEXT("Encrypted opcodes\tCtrl+E")); + AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_COMMENTS, TEXT("Comments\tCtrl+N")); + AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options")); + disasm_update_checkmarks(info); + + // set the handlers + info->handle_command = disasm_handle_command; + info->handle_key = disasm_handle_key; + + // lock us to the bottom of the console by default + info->view[2].is_textbuf = TRUE; + + // set up the disassembly view to track the current pc + debug_view_begin_update(info->view[0].view); + debug_view_set_property_string(info->view[0].view, DVP_DASM_EXPRESSION, "curpc"); + debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_TRACK_LIVE, 1); + debug_view_end_update(info->view[0].view); + + // create an edit box and override its key handling + info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE, + 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL); + info->original_editproc = (void *)(FPTR)GetWindowLongPtr(info->editwnd, GWLP_WNDPROC); + SetWindowLongPtr(info->editwnd, GWLP_USERDATA, (LONG_PTR)info); + SetWindowLongPtr(info->editwnd, GWLP_WNDPROC, (LONG_PTR)debug_edit_proc); + SendMessage(info->editwnd, WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE); + SendMessage(info->editwnd, EM_LIMITTEXT, (WPARAM)MAX_EDIT_STRING, (LPARAM)0); + + // set the child functions + info->recompute_children = console_recompute_children; + info->process_string = console_process_string; + + // loop over all CPUs and compute the sizes + info->minwidth = 0; + info->maxwidth = 0; + for (cpunum = MAX_CPU - 1; (INT32)cpunum >= 0; cpunum--) + if (Machine->drv->cpu[cpunum].type != CPU_DUMMY) + { + UINT32 regchars, dischars, conchars; + UINT32 minwidth, maxwidth; + + // point all views to the new CPU number + debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM, cpunum); + debug_view_set_property_UINT32(info->view[1].view, DVP_REGS_CPUNUM, cpunum); + + // get the total width of all three children + dischars = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS); + regchars = debug_view_get_property_UINT32(info->view[1].view, DVP_TOTAL_COLS); + conchars = debug_view_get_property_UINT32(info->view[2].view, DVP_TOTAL_COLS); + + // compute the preferred width + minwidth = EDGE_WIDTH + regchars * debug_font_width + vscroll_width + 2 * EDGE_WIDTH + 100 + EDGE_WIDTH; + maxwidth = EDGE_WIDTH + regchars * debug_font_width + vscroll_width + 2 * EDGE_WIDTH + ((dischars > conchars) ? dischars : conchars) * debug_font_width + vscroll_width + EDGE_WIDTH; + if (minwidth > info->minwidth) + info->minwidth = minwidth; + if (maxwidth > info->maxwidth) + info->maxwidth = maxwidth; + } + + // get the work bounds + SystemParametersInfo(SPI_GETWORKAREA, 0, &work_bounds, 0); + + // adjust the min/max sizes for the window style + bounds.top = bounds.left = 0; + bounds.right = bounds.bottom = info->minwidth; + AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX); + info->minwidth = bounds.right - bounds.left; + + bounds.top = bounds.left = 0; + bounds.right = bounds.bottom = info->maxwidth; + AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX); + info->maxwidth = bounds.right - bounds.left; + + // position the window at the bottom-right + bestwidth = (info->maxwidth < (work_bounds.right - work_bounds.left)) ? info->maxwidth : (work_bounds.right - work_bounds.left); + bestheight = (500 < (work_bounds.bottom - work_bounds.top)) ? 500 : (work_bounds.bottom - work_bounds.top); + SetWindowPos(info->wnd, HWND_TOP, + work_bounds.right - bestwidth, work_bounds.bottom - bestheight, + bestwidth, bestheight, + SWP_SHOWWINDOW); + + // recompute the children + console_recompute_children(info); + + // mark the edit box as the default focus and set it + info->focuswnd = info->editwnd; + SetFocus(info->editwnd); + return; + +cleanup: + if (info->view[2].view) + debug_view_free(info->view[2].view); + if (info->view[1].view) + debug_view_free(info->view[1].view); + if (info->view[0].view) + debug_view_free(info->view[0].view); +} + + + +//============================================================ +// console_recompute_children +//============================================================ + +static void console_recompute_children(debugwin_info *info) +{ + RECT parent, regrect, disrect, conrect, editrect; + UINT32 regchars, dischars, conchars; + + // get the parent's dimensions + GetClientRect(info->wnd, &parent); + + // get the total width of all three children + dischars = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS); + regchars = debug_view_get_property_UINT32(info->view[1].view, DVP_TOTAL_COLS); + conchars = debug_view_get_property_UINT32(info->view[2].view, DVP_TOTAL_COLS); + + // registers always get their desired width, and span the entire height + regrect.top = parent.top + EDGE_WIDTH; + regrect.bottom = parent.bottom - EDGE_WIDTH; + regrect.left = parent.left + EDGE_WIDTH; + regrect.right = regrect.left + debug_font_width * regchars + vscroll_width; + + // edit box goes at the bottom of the remaining area + editrect.bottom = parent.bottom - EDGE_WIDTH; + editrect.top = editrect.bottom - debug_font_height - 4; + editrect.left = regrect.right + EDGE_WIDTH * 2; + editrect.right = parent.right - EDGE_WIDTH; + + // console and disassembly split the difference + disrect.top = parent.top + EDGE_WIDTH; + disrect.bottom = (editrect.top - parent.top) / 2 - EDGE_WIDTH; + disrect.left = regrect.right + EDGE_WIDTH * 2; + disrect.right = parent.right - EDGE_WIDTH; + + conrect.top = disrect.bottom + EDGE_WIDTH * 2; + conrect.bottom = editrect.top - EDGE_WIDTH; + conrect.left = regrect.right + EDGE_WIDTH * 2; + conrect.right = parent.right - EDGE_WIDTH; + + // set the bounds of things + debug_view_set_bounds(&info->view[0], info->wnd, &disrect); + debug_view_set_bounds(&info->view[1], info->wnd, ®rect); + debug_view_set_bounds(&info->view[2], info->wnd, &conrect); + smart_set_window_bounds(info->editwnd, info->wnd, &editrect); +} + + + +//============================================================ +// console_process_string +//============================================================ + +static void console_process_string(debugwin_info *info, const char *string) +{ + TCHAR buffer = 0; + + // an empty string is a single step + if (string[0] == 0) + debug_cpu_single_step(1); + + // otherwise, just process the command + else + debug_console_execute_command(string, 1); + + // clear the edit text box + SendMessage(info->editwnd, WM_SETTEXT, 0, (LPARAM)&buffer); +} + + + +//============================================================ +// console_set_cpunum +//============================================================ + +static void console_set_cpunum(int cpunum) +{ + char title[256], curtitle[256]; + + // first set all the views to the new cpu number + if (main_console->view[0].view) + debug_view_set_property_UINT32(main_console->view[0].view, DVP_DASM_CPUNUM, cpunum); + if (main_console->view[1].view) + debug_view_set_property_UINT32(main_console->view[1].view, DVP_REGS_CPUNUM, cpunum); + + // then update the caption + snprintf(title, ARRAY_LENGTH(title), "Debug: %s - CPU %d (%s)", Machine->gamedrv->name, cpu_getactivecpu(), activecpu_name()); + win_get_window_text_utf8(main_console->wnd, curtitle, sizeof(curtitle) / sizeof(curtitle[0])); + if (strcmp(title, curtitle)) + win_set_window_text_utf8(main_console->wnd, title); +} + + + +//============================================================ +// create_standard_menubar +//============================================================ + +static HMENU create_standard_menubar(void) +{ + HMENU menubar, debugmenu; + + // create the debug menu + debugmenu = CreatePopupMenu(); + if (debugmenu == NULL) + return NULL; + AppendMenu(debugmenu, MF_ENABLED, ID_NEW_MEMORY_WND, TEXT("New Memory Window\tCtrl+M")); + AppendMenu(debugmenu, MF_ENABLED, ID_NEW_DISASM_WND, TEXT("New Disassembly Window\tCtrl+D")); + AppendMenu(debugmenu, MF_ENABLED, ID_NEW_LOG_WND, TEXT("New Error Log Window\tCtrl+L")); + AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); + AppendMenu(debugmenu, MF_ENABLED, ID_RUN, TEXT("Run\tF5")); + AppendMenu(debugmenu, MF_ENABLED, ID_RUN_AND_HIDE, TEXT("Run and Hide Debugger\tF12")); + AppendMenu(debugmenu, MF_ENABLED, ID_NEXT_CPU, TEXT("Run to Next CPU\tF6")); + AppendMenu(debugmenu, MF_ENABLED, ID_RUN_IRQ, TEXT("Run until Next Interrupt on This CPU\tF7")); + AppendMenu(debugmenu, MF_ENABLED, ID_RUN_VBLANK, TEXT("Run until Next VBLANK\tF8")); + AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); + AppendMenu(debugmenu, MF_ENABLED, ID_STEP, TEXT("Step Into\tF11")); + AppendMenu(debugmenu, MF_ENABLED, ID_STEP_OVER, TEXT("Step Over\tF10")); + AppendMenu(debugmenu, MF_ENABLED, ID_STEP_OUT, TEXT("Step Out\tShift+F11")); + AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); + AppendMenu(debugmenu, MF_ENABLED, ID_SOFT_RESET, TEXT("Soft Reset\tF3")); + AppendMenu(debugmenu, MF_ENABLED, ID_HARD_RESET, TEXT("Hard Reset\tShift+F3")); + AppendMenu(debugmenu, MF_ENABLED, ID_EXIT, TEXT("Exit")); + + // create the menu bar + menubar = CreateMenu(); + if (menubar == NULL) + { + DestroyMenu(debugmenu); + return NULL; + } + AppendMenu(menubar, MF_ENABLED | MF_POPUP, (UINT_PTR)debugmenu, TEXT("Debug")); + + return menubar; +} + + + +//============================================================ +// global_handle_command +//============================================================ + +static int global_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam) +{ + if (HIWORD(wparam) == 0) + switch (LOWORD(wparam)) + { + case ID_NEW_MEMORY_WND: + memory_create_window(); + return 1; + + case ID_NEW_DISASM_WND: + disasm_create_window(); + return 1; + + case ID_NEW_LOG_WND: + log_create_window(); + return 1; + + case ID_RUN_AND_HIDE: + smart_show_all(FALSE); + case ID_RUN: + debug_cpu_go(~0); + return 1; + + case ID_NEXT_CPU: + debug_cpu_next_cpu(); + return 1; + + case ID_RUN_VBLANK: + debug_cpu_go_vblank(); + return 1; + + case ID_RUN_IRQ: + debug_cpu_go_interrupt(-1); + return 1; + + case ID_STEP: + debug_cpu_single_step(1); + return 1; + + case ID_STEP_OVER: + debug_cpu_single_step_over(1); + return 1; + + case ID_STEP_OUT: + debug_cpu_single_step_out(); + return 1; + + case ID_HARD_RESET: + mame_schedule_hard_reset(Machine); + return 1; + + case ID_SOFT_RESET: + mame_schedule_soft_reset(Machine); + debug_cpu_go(~0); + return 1; + + case ID_EXIT: + mame_schedule_exit(Machine); + return 1; + } + + return 0; +} + + + +//============================================================ +// global_handle_key +//============================================================ + +static int global_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam) +{ + int ignoreme; + + /* ignore any keys that are received while the debug key is down */ + ignoreme = input_ui_pressed(IPT_UI_DEBUG_BREAK); + if (ignoreme) + return 1; + + switch (wparam) + { + case VK_F3: + if (GetAsyncKeyState(VK_SHIFT) & 0x8000) + SendMessage(info->wnd, WM_COMMAND, ID_HARD_RESET, 0); + else + SendMessage(info->wnd, WM_COMMAND, ID_SOFT_RESET, 0); + return 1; + + case VK_F4: + if (GetAsyncKeyState(VK_MENU) & 0x8000) + { + /* ajg - never gets here since 'alt' seems to be captured somewhere else - menu maybe? */ + SendMessage(info->wnd, WM_COMMAND, ID_EXIT, 0); + return 1; + } + break; + + case VK_F5: + SendMessage(info->wnd, WM_COMMAND, ID_RUN, 0); + return 1; + + case VK_F6: + SendMessage(info->wnd, WM_COMMAND, ID_NEXT_CPU, 0); + return 1; + + case VK_F7: + SendMessage(info->wnd, WM_COMMAND, ID_RUN_IRQ, 0); + return 1; + + case VK_F8: + SendMessage(info->wnd, WM_COMMAND, ID_RUN_VBLANK, 0); + return 1; + + case VK_F10: + SendMessage(info->wnd, WM_COMMAND, ID_STEP_OVER, 0); + return 1; + + case VK_F11: + if (GetAsyncKeyState(VK_SHIFT) & 0x8000) + SendMessage(info->wnd, WM_COMMAND, ID_STEP_OUT, 0); + else + SendMessage(info->wnd, WM_COMMAND, ID_STEP, 0); + return 1; + + case VK_F12: + SendMessage(info->wnd, WM_COMMAND, ID_RUN_AND_HIDE, 0); + return 1; + + case 'M': + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + { + SendMessage(info->wnd, WM_COMMAND, ID_NEW_MEMORY_WND, 0); + return 1; + } + break; + + case 'D': + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + { + SendMessage(info->wnd, WM_COMMAND, ID_NEW_DISASM_WND, 0); + return 1; + } + break; + + case 'L': + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) + { + SendMessage(info->wnd, WM_COMMAND, ID_NEW_LOG_WND, 0); + return 1; + } + break; + } + + return 0; +} + + + +//============================================================ +// smart_set_window_bounds +//============================================================ + +static void smart_set_window_bounds(HWND wnd, HWND parent, RECT *bounds) +{ + RECT curbounds; + int flags = 0; + + // first get the current bounds, relative to the parent + GetWindowRect(wnd, &curbounds); + if (parent != NULL) + { + RECT parentbounds; + GetWindowRect(parent, &parentbounds); + curbounds.top -= parentbounds.top; + curbounds.bottom -= parentbounds.top; + curbounds.left -= parentbounds.left; + curbounds.right -= parentbounds.left; + } + + // if the position matches, don't change it + if (curbounds.top == bounds->top && curbounds.left == bounds->left) + flags |= SWP_NOMOVE; + if ((curbounds.bottom - curbounds.top) == (bounds->bottom - bounds->top) && + (curbounds.right - curbounds.left) == (bounds->right - bounds->left)) + flags |= SWP_NOSIZE; + + // if we need to, reposition the window + if (flags != (SWP_NOMOVE | SWP_NOSIZE)) + SetWindowPos(wnd, NULL, + bounds->left, bounds->top, + bounds->right - bounds->left, bounds->bottom - bounds->top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | flags); +} + + + +//============================================================ +// smart_show_window +//============================================================ + +static void smart_show_window(HWND wnd, BOOL show) +{ + BOOL visible = IsWindowVisible(wnd); + if ((visible && !show) || (!visible && show)) + ShowWindow(wnd, show ? SW_SHOW : SW_HIDE); +} + + + +//============================================================ +// smart_show_all +//============================================================ + +static void smart_show_all(BOOL show) +{ + debugwin_info *info; + if (!show) + SetForegroundWindow(win_window_list->hwnd); + for (info = window_list; info; info = info->next) + smart_show_window(info->wnd, show); +} diff --git a/src/osd/windows/debugwin.h b/src/osd/windows/debugwin.h new file mode 100644 index 00000000000..7033f3b772e --- /dev/null +++ b/src/osd/windows/debugwin.h @@ -0,0 +1,32 @@ +//============================================================ +// +// debugwin.h - Win32 debug window handling +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __WIN_DEBUGWIN__ +#define __WIN_DEBUGWIN__ + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +// windows + + + +//============================================================ +// PROTOTYPES +//============================================================ + +void debugwin_init_windows(void); +void debugwin_destroy_windows(void); +void debugwin_show(int type); +void debugwin_update_during_game(void); +int debugwin_is_debugger_visible(void); + +#endif diff --git a/src/osd/windows/drawd3d.c b/src/osd/windows/drawd3d.c new file mode 100644 index 00000000000..e819bf7f0e2 --- /dev/null +++ b/src/osd/windows/drawd3d.c @@ -0,0 +1,2421 @@ +//============================================================ +// +// drawd3d.c - Win32 Direct3D implementation +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// Useful info: +// Windows XP/2003 shipped with DirectX 8.1 +// Windows 2000 shipped with DirectX 7a +// Windows 98SE shipped with DirectX 6.1a +// Windows 98 shipped with DirectX 5 +// Windows NT shipped with DirectX 3.0a +// Windows 95 shipped with DirectX 2 + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +// standard C headers +#include + +// MAME headers +#include "render.h" +#include "rendutil.h" +#include "options.h" + +// MAMEOS headers +#include "d3dintf.h" +#include "winmain.h" +#include "window.h" +#include "config.h" +#include "strconv.h" + + + +//============================================================ +// DEBUGGING +//============================================================ + +extern void mtlog_add(const char *event); + + + +//============================================================ +// CONSTANTS +//============================================================ + +#define ENABLE_BORDER_PIX (1) + +#define VERTEX_FORMAT (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1) +#define VERTEX_BUFFER_SIZE (2048*4) + +enum +{ + TEXTURE_TYPE_PLAIN, + TEXTURE_TYPE_DYNAMIC, + TEXTURE_TYPE_SURFACE +}; + + + +//============================================================ +// MACROS +//============================================================ + +#define FSWAP(var1, var2) do { float temp = var1; var1 = var2; var2 = temp; } while (0) + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +/* texture_info holds information about a texture */ +typedef struct _texture_info texture_info; +struct _texture_info +{ + texture_info * next; // next texture in the list + UINT32 hash; // hash value for the texture + UINT32 flags; // rendering flags + render_texinfo texinfo; // copy of the texture info + float ustart, ustop; // beginning/ending U coordinates + float vstart, vstop; // beginning/ending V coordinates + int rawwidth, rawheight; // raw width/height of the texture + int type; // what type of texture are we? + int borderpix; // do we have a 1 pixel border? + int xprescale; // what is our X prescale factor? + int yprescale; // what is our Y prescale factor? + d3d_texture * d3dtex; // Direct3D texture pointer + d3d_surface * d3dsurface; // Direct3D offscreen plain surface pointer + d3d_texture * d3dfinaltex; // Direct3D final (post-scaled) texture +}; + + +/* poly_info holds information about a single polygon/d3d primitive */ +typedef struct _poly_info poly_info; +struct _poly_info +{ + D3DPRIMITIVETYPE type; // type of primitive + UINT32 count; // total number of primitives + UINT32 numverts; // total number of vertices + UINT32 flags; // rendering flags + DWORD modmode; // texture modulation mode + texture_info * texture; // pointer to texture info +}; + + +/* d3d_vertex describes a single vertex */ +typedef struct _d3d_vertex d3d_vertex; +struct _d3d_vertex +{ + float x, y, z; // X,Y,Z coordinates + float rhw; // 1/W coordinate + D3DCOLOR color; // diffuse color + float u0, v0; // texture stage 0 coordinates +}; + + +/* d3d_info is the information about Direct3D for the current screen */ +typedef struct _d3d_info d3d_info; +struct _d3d_info +{ + int adapter; // ordinal adapter number + int width, height; // current width, height + int refresh; // current refresh rate + + d3d_device * device; // pointer to the Direct3DDevice object + int gamma_supported; // is full screen gamma supported? + d3d_present_parameters presentation; // set of presentation parameters + D3DDISPLAYMODE origmode; // original display mode for the adapter + D3DFORMAT pixformat; // pixel format we are using + + d3d_vertex_buffer * vertexbuf; // pointer to the vertex buffer object + d3d_vertex * lockedbuf; // pointer to the locked vertex buffer + int numverts; // number of accumulated vertices + + poly_info poly[VERTEX_BUFFER_SIZE / 3];// array to hold polygons as they are created + int numpolys; // number of accumulated polygons + + texture_info * texlist; // list of active textures + int dynamic_supported; // are dynamic textures supported? + int stretch_supported; // is StretchRect with point filtering supported? + int mod2x_supported; // is D3DTOP_MODULATE2X supported? + int mod4x_supported; // is D3DTOP_MODULATE4X supported? + D3DFORMAT screen_format; // format to use for screen textures + D3DFORMAT yuv_format; // format to use for YUV textures + + DWORD texture_caps; // textureCaps field + DWORD texture_max_aspect; // texture maximum aspect ratio + DWORD texture_max_width; // texture maximum width + DWORD texture_max_height; // texture maximum height + + texture_info * last_texture; // previous texture + int last_blendenable; // previous blendmode + int last_blendop; // previous blendmode + int last_blendsrc; // previous blendmode + int last_blenddst; // previous blendmode + int last_filter; // previous texture filter + int last_wrap; // previous wrap state + DWORD last_modmode; // previous texture modulation + + mame_bitmap * vector_bitmap; // experimental: bitmap for vectors + texture_info * vector_texture; // experimental: texture for vectors +}; + + +/* line_aa_step is used for drawing antialiased lines */ +typedef struct _line_aa_step line_aa_step; +struct _line_aa_step +{ + float xoffs, yoffs; // X/Y deltas + float weight; // weight contribution +}; + + + +//============================================================ +// GLOBALS +//============================================================ + +static d3d * d3dintf; + +static const line_aa_step line_aa_1step[] = +{ + { 0.00f, 0.00f, 1.00f }, + { 0 } +}; + +static const line_aa_step line_aa_4step[] = +{ + { -0.25f, 0.00f, 0.25f }, + { 0.25f, 0.00f, 0.25f }, + { 0.00f, -0.25f, 0.25f }, + { 0.00f, 0.25f, 0.25f }, + { 0 } +}; + + + +//============================================================ +// INLINES +//============================================================ + +INLINE UINT32 ycc_to_rgb(UINT8 y, UINT8 cb, UINT8 cr) +{ + /* original equations: + + C = Y - 16 + D = Cb - 128 + E = Cr - 128 + + R = clip(( 298 * C + 409 * E + 128) >> 8) + G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8) + B = clip(( 298 * C + 516 * D + 128) >> 8) + + R = clip(( 298 * (Y - 16) + 409 * (Cr - 128) + 128) >> 8) + G = clip(( 298 * (Y - 16) - 100 * (Cb - 128) - 208 * (Cr - 128) + 128) >> 8) + B = clip(( 298 * (Y - 16) + 516 * (Cb - 128) + 128) >> 8) + + R = clip(( 298 * Y - 298 * 16 + 409 * Cr - 409 * 128 + 128) >> 8) + G = clip(( 298 * Y - 298 * 16 - 100 * Cb + 100 * 128 - 208 * Cr + 208 * 128 + 128) >> 8) + B = clip(( 298 * Y - 298 * 16 + 516 * Cb - 516 * 128 + 128) >> 8) + + R = clip(( 298 * Y - 298 * 16 + 409 * Cr - 409 * 128 + 128) >> 8) + G = clip(( 298 * Y - 298 * 16 - 100 * Cb + 100 * 128 - 208 * Cr + 208 * 128 + 128) >> 8) + B = clip(( 298 * Y - 298 * 16 + 516 * Cb - 516 * 128 + 128) >> 8) + */ + int r, g, b, common; + + common = 298 * y - 298 * 16; + r = (common + 409 * cr - 409 * 128 + 128) >> 8; + g = (common - 100 * cb + 100 * 128 - 208 * cr + 208 * 128 + 128) >> 8; + b = (common + 516 * cb - 516 * 128 + 128) >> 8; + + if (r < 0) r = 0; + else if (r > 255) r = 255; + if (g < 0) g = 0; + else if (g > 255) g = 255; + if (b < 0) b = 0; + else if (b > 255) b = 255; + + return MAKE_ARGB(0xff, r, g, b); +} + + +INLINE UINT32 texture_compute_hash(const render_texinfo *texture, UINT32 flags) +{ + return (FPTR)texture->base ^ (flags & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)); +} + + +INLINE void set_texture(d3d_info *d3d, texture_info *texture) +{ + HRESULT result; + if (texture != d3d->last_texture) + { + d3d->last_texture = texture; + result = (*d3dintf->device.set_texture)(d3d->device, 0, (texture == NULL) ? NULL : texture->d3dfinaltex); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture call\n", (int)result); + } +} + + +INLINE void set_filter(d3d_info *d3d, int filter) +{ + HRESULT result; + if (filter != d3d->last_filter) + { + d3d->last_filter = filter; + result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_MINFILTER, filter ? D3DTEXF_LINEAR : D3DTEXF_POINT); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result); + result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_MAGFILTER, filter ? D3DTEXF_LINEAR : D3DTEXF_POINT); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result); + } +} + + +INLINE void set_wrap(d3d_info *d3d, int wrap) +{ + HRESULT result; + if (wrap != d3d->last_wrap) + { + d3d->last_wrap = wrap; + result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_ADDRESSU, wrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result); + result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_ADDRESSV, wrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result); + } +} + + +INLINE void set_modmode(d3d_info *d3d, DWORD modmode) +{ + HRESULT result; + if (modmode != d3d->last_modmode) + { + d3d->last_modmode = modmode; + result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_COLOROP, modmode); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result); + } +} + + +INLINE void set_blendmode(d3d_info *d3d, int blendmode) +{ + HRESULT result; + int blendenable; + int blendop; + int blendsrc; + int blenddst; + + // choose the parameters + switch (blendmode) + { + default: + case BLENDMODE_NONE: blendenable = FALSE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_SRCALPHA; blenddst = D3DBLEND_INVSRCALPHA; break; + case BLENDMODE_ALPHA: blendenable = TRUE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_SRCALPHA; blenddst = D3DBLEND_INVSRCALPHA; break; + case BLENDMODE_RGB_MULTIPLY: blendenable = TRUE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_DESTCOLOR; blenddst = D3DBLEND_ZERO; break; + case BLENDMODE_ADD: blendenable = TRUE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_SRCALPHA; blenddst = D3DBLEND_ONE; break; + } + + // adjust the bits that changed + if (blendenable != d3d->last_blendenable) + { + d3d->last_blendenable = blendenable; + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHABLENDENABLE, blendenable); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result); + } + + if (blendop != d3d->last_blendop) + { + d3d->last_blendop = blendop; + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_BLENDOP, blendop); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result); + } + + if (blendsrc != d3d->last_blendsrc) + { + d3d->last_blendsrc = blendsrc; + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_SRCBLEND, blendsrc); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result); + } + + if (blenddst != d3d->last_blenddst) + { + d3d->last_blenddst = blenddst; + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_DESTBLEND, blenddst); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result); + } +} + + +INLINE void reset_render_states(d3d_info *d3d) +{ + // this ensures subsequent calls to the above setters will force-update the data + d3d->last_texture = (texture_info *)~0; + d3d->last_filter = -1; + d3d->last_blendenable = -1; + d3d->last_blendop = -1; + d3d->last_blendsrc = -1; + d3d->last_blenddst = -1; + d3d->last_wrap = -1; +} + + + +//============================================================ +// PROTOTYPES +//============================================================ + +// core functions +static void drawd3d_exit(void); +static int drawd3d_window_init(win_window_info *window); +static void drawd3d_window_destroy(win_window_info *window); +static const render_primitive_list *drawd3d_window_get_primitives(win_window_info *window); +static int drawd3d_window_draw(win_window_info *window, HDC dc, int update); + +// devices +static int device_create(win_window_info *window); +static int device_create_resources(d3d_info *d3d); +static void device_delete(d3d_info *d3d); +static void device_delete_resources(d3d_info *d3d); +static int device_verify_caps(d3d_info *d3d); +static int device_test_cooperative(d3d_info *d3d); + +// video modes +static int config_adapter_mode(win_window_info *window); +static int get_adapter_for_monitor(d3d_info *d3d, win_monitor_info *monitor); +static void pick_best_mode(win_window_info *window); +static int update_window_size(win_window_info *window); + +// drawing +static void draw_line(d3d_info *d3d, const render_primitive *prim); +static void draw_quad(d3d_info *d3d, const render_primitive *prim); + +// primitives +static d3d_vertex *primitive_alloc(d3d_info *d3d, int numverts); +static void primitive_flush_pending(d3d_info *d3d); + +// textures +static texture_info *texture_create(d3d_info *d3d, const render_texinfo *texsource, UINT32 flags); +static void texture_compute_size(d3d_info *d3d, int texwidth, int texheight, texture_info *texture); +static void texture_set_data(d3d_info *d3d, texture_info *texture, const render_texinfo *texsource, UINT32 flags); +static void texture_prescale(d3d_info *d3d, texture_info *texture); +static texture_info *texture_find(d3d_info *d3d, const render_primitive *prim); +static void texture_update(d3d_info *d3d, const render_primitive *prim); + + + +//============================================================ +// drawd3d_init +//============================================================ + +int drawd3d_init(win_draw_callbacks *callbacks) +{ + int version = options_get_int(mame_options(), WINOPTION_D3DVERSION); + d3dintf = NULL; + + // try Direct3D 9 if requested + if (version >= 9) + d3dintf = drawd3d9_init(); + + // if that didn't work, try Direct3D 8 + if (d3dintf == NULL && version >= 8) + d3dintf = drawd3d8_init(); + + // if we failed, note the error + if (d3dintf == NULL) + { + mame_printf_error("Unable to initialize Direct3D.\n"); + return 1; + } + + // fill in the callbacks + callbacks->exit = drawd3d_exit; + callbacks->window_init = drawd3d_window_init; + callbacks->window_get_primitives = drawd3d_window_get_primitives; + callbacks->window_draw = drawd3d_window_draw; + callbacks->window_destroy = drawd3d_window_destroy; + return 0; +} + + + +//============================================================ +// drawd3d_exit +//============================================================ + +static void drawd3d_exit(void) +{ + if (d3dintf != NULL) + (*d3dintf->d3d.release)(d3dintf); +} + + + +//============================================================ +// drawd3d_window_init +//============================================================ + +static int drawd3d_window_init(win_window_info *window) +{ + d3d_info *d3d; + + // allocate memory for our structures + d3d = malloc_or_die(sizeof(*d3d)); + memset(d3d, 0, sizeof(*d3d)); + window->drawdata = d3d; + + // experimental: load a PNG to use for vector rendering; it is treated + // as a brightness map + d3d->vector_bitmap = render_load_png(NULL, "vector.png", NULL, NULL); + if (d3d->vector_bitmap != NULL) + { + fillbitmap(d3d->vector_bitmap, MAKE_ARGB(0xff,0xff,0xff,0xff), NULL); + d3d->vector_bitmap = render_load_png(NULL, "vector.png", d3d->vector_bitmap, NULL); + } + + // configure the adapter for the mode we want + if (config_adapter_mode(window)) + goto error; + + // create the device immediately for the full screen case (defer for window mode) + if (window->fullscreen && device_create(window)) + goto error; + + return 0; + +error: + drawd3d_window_destroy(window); + mame_printf_error("Unable to initialize Direct3D.\n"); + return 1; +} + + + +//============================================================ +// drawd3d_window_destroy +//============================================================ + +static void drawd3d_window_destroy(win_window_info *window) +{ + d3d_info *d3d = window->drawdata; + + // skip if nothing + if (d3d == NULL) + return; + + // delete the device + device_delete(d3d); + + // experimental: free the vector PNG + if (d3d->vector_bitmap != NULL) + bitmap_free(d3d->vector_bitmap); + + // free the memory in the window + free(d3d); + window->drawdata = NULL; +} + + + +//============================================================ +// drawd3d_window_get_primitives +//============================================================ + +static const render_primitive_list *drawd3d_window_get_primitives(win_window_info *window) +{ + d3d_info *d3d = window->drawdata; + RECT client; + + GetClientRect(window->hwnd, &client); + if (rect_width(&client) > 0 && rect_height(&client) > 0) + { + render_target_set_bounds(window->target, rect_width(&client), rect_height(&client), winvideo_monitor_get_aspect(window->monitor)); + render_target_set_max_update_rate(window->target, (d3d->refresh == 0) ? d3d->origmode.RefreshRate : d3d->refresh); + } + return render_target_get_primitives(window->target); +} + + + +//============================================================ +// drawd3d_window_draw +//============================================================ + +static int drawd3d_window_draw(win_window_info *window, HDC dc, int update) +{ + d3d_info *d3d = window->drawdata; + const render_primitive *prim; + HRESULT result; + + // if we haven't been created, just punt + if (d3d == NULL) + return 1; + + // if we have a device, check the cooperative level + if (d3d->device != NULL) + { + int error = device_test_cooperative(d3d); + if (error) + return 1; + } + + // in window mode, we need to track the window size + if (!window->fullscreen || d3d->device == NULL) + { + // if the size changes, skip this update since the render target will be out of date + if (update_window_size(window)) + return 0; + + // if we have no device, after updating the size, return an error so GDI can try + if (d3d->device == NULL) + return 1; + } + +mtlog_add("drawd3d_window_draw: begin"); + + // first update any textures + osd_lock_acquire(window->primlist->lock); + for (prim = window->primlist->head; prim != NULL; prim = prim->next) + if (prim->texture.base != NULL) + texture_update(d3d, prim); + + // begin the scene +mtlog_add("drawd3d_window_draw: begin_scene"); + result = (*d3dintf->device.begin_scene)(d3d->device); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device begin_scene call\n", (int)result); + + d3d->lockedbuf = NULL; + + // loop over primitives +mtlog_add("drawd3d_window_draw: primitive loop begin"); + for (prim = window->primlist->head; prim != NULL; prim = prim->next) + switch (prim->type) + { + case RENDER_PRIMITIVE_LINE: + draw_line(d3d, prim); + break; + + case RENDER_PRIMITIVE_QUAD: + draw_quad(d3d, prim); + break; + } +mtlog_add("drawd3d_window_draw: primitive loop end"); + osd_lock_release(window->primlist->lock); + + // flush any pending polygons +mtlog_add("drawd3d_window_draw: flush_pending begin"); + primitive_flush_pending(d3d); +mtlog_add("drawd3d_window_draw: flush_pending end"); + + // finish the scene +mtlog_add("drawd3d_window_draw: end_scene begin"); + result = (*d3dintf->device.end_scene)(d3d->device); +mtlog_add("drawd3d_window_draw: end_scene end"); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device end_scene call\n", (int)result); + + // present the current buffers +mtlog_add("drawd3d_window_draw: present begin"); + result = (*d3dintf->device.present)(d3d->device, NULL, NULL, NULL, NULL, 0); +mtlog_add("drawd3d_window_draw: present end"); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device present call\n", (int)result); + return 0; +} + + + +//============================================================ +// device_create +//============================================================ + +static int device_create(win_window_info *window) +{ + d3d_info *d3d = window->drawdata; + HRESULT result; + int verify; + + // if a device exists, free it + if (d3d->device != NULL) + device_delete(d3d); + + // verify the caps + verify = device_verify_caps(d3d); + if (verify == 2) + { + mame_printf_error("Error: Device does not meet minimum requirements for Direct3D rendering\n"); + return 1; + } + if (verify == 1) + mame_printf_warning("Warning: Device may not perform well for Direct3D rendering\n"); + + // verify texture formats + result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8); + if (result != D3D_OK) + { + mame_printf_error("Error: A8R8G8B8 format textures not supported\n"); + return 1; + } + + // pick a YUV texture format + d3d->yuv_format = D3DFMT_UYVY; + result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_UYVY); + if (result != D3D_OK) + { + d3d->yuv_format = D3DFMT_YUY2; + result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_YUY2); + if (result != D3D_OK) + d3d->yuv_format = D3DFMT_A8R8G8B8; + } + mame_printf_verbose("Direct3D: YUV format = %s\n", (d3d->yuv_format == D3DFMT_YUY2) ? "YUY2" : (d3d->yuv_format == D3DFMT_UYVY) ? "UYVY" : "RGB"); + +try_again: + // try for XRGB first + d3d->screen_format = D3DFMT_X8R8G8B8; + result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, d3d->dynamic_supported ? D3DUSAGE_DYNAMIC : 0, D3DRTYPE_TEXTURE, d3d->screen_format); + if (result != D3D_OK) + { + // if not, try for ARGB + d3d->screen_format = D3DFMT_A8R8G8B8; + result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, d3d->dynamic_supported ? D3DUSAGE_DYNAMIC : 0, D3DRTYPE_TEXTURE, d3d->screen_format); + if (result != D3D_OK && d3d->dynamic_supported) + { + d3d->dynamic_supported = FALSE; + goto try_again; + } + if (result != D3D_OK) + { + mame_printf_error("Error: unable to configure a screen texture format\n"); + return 1; + } + } + + // initialize the D3D presentation parameters + memset(&d3d->presentation, 0, sizeof(d3d->presentation)); + d3d->presentation.BackBufferWidth = d3d->width; + d3d->presentation.BackBufferHeight = d3d->height; + d3d->presentation.BackBufferFormat = d3d->pixformat; + d3d->presentation.BackBufferCount = video_config.triplebuf ? 2 : 1; + d3d->presentation.MultiSampleType = D3DMULTISAMPLE_NONE; + d3d->presentation.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3d->presentation.hDeviceWindow = window->hwnd; + d3d->presentation.Windowed = !window->fullscreen || HAS_WINDOW_MENU; + d3d->presentation.EnableAutoDepthStencil = FALSE; + d3d->presentation.AutoDepthStencilFormat = D3DFMT_D16; + d3d->presentation.Flags = 0; + d3d->presentation.FullScreen_RefreshRateInHz = d3d->refresh; + d3d->presentation.PresentationInterval = ((video_config.triplebuf && window->fullscreen) || video_config.waitvsync || video_config.syncrefresh) ? + D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; + // create the D3D device + result = (*d3dintf->d3d.create_device)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, win_window_list->hwnd, + D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &d3d->presentation, &d3d->device); + if (result != D3D_OK) + { + mame_printf_error("Unable to create the Direct3D device (%08X)\n", (UINT32)result); + return 1; + } + mame_printf_verbose("Direct3D: Device created at %dx%d\n", d3d->width, d3d->height); + + // set the max texture size + render_target_set_max_texture_size(window->target, d3d->texture_max_width, d3d->texture_max_height); + + // set the gamma if we need to + if (window->fullscreen) + { + // only set the gamma if it's not 1.0f + float brightness = options_get_float(mame_options(), WINOPTION_FULLSCREENBRIGHTNESS); + float contrast = options_get_float(mame_options(), WINOPTION_FULLLSCREENCONTRAST); + float gamma = options_get_float(mame_options(), WINOPTION_FULLSCREENGAMMA); + if (brightness != 1.0f || contrast != 1.0f || gamma != 1.0f) + { + // warn if we can't do it + if (!d3d->gamma_supported) + mame_printf_warning("Direct3D: Warning - device does not support full screen gamma correction.\n"); + else + { + D3DGAMMARAMP ramp; + int i; + + // create a standard ramp and set it + for (i = 0; i < 256; i++) + ramp.red[i] = ramp.green[i] = ramp.blue[i] = apply_brightness_contrast_gamma(i, brightness, contrast, gamma) << 8; + (*d3dintf->device.set_gamma_ramp)(d3d->device, 0, &ramp); + } + } + } + + return device_create_resources(d3d); +} + + + +//============================================================ +// device_create_resources +//============================================================ + +static int device_create_resources(d3d_info *d3d) +{ + HRESULT result; + + // allocate a vertex buffer to use + result = (*d3dintf->device.create_vertex_buffer)(d3d->device, + sizeof(d3d_vertex) * VERTEX_BUFFER_SIZE, + D3DUSAGE_DYNAMIC | D3DUSAGE_SOFTWAREPROCESSING | D3DUSAGE_WRITEONLY, + VERTEX_FORMAT, D3DPOOL_DEFAULT, &d3d->vertexbuf); + if (result != D3D_OK) + { + mame_printf_error("Error creating vertex buffer (%08X)", (UINT32)result); + return 1; + } + + // set the vertex format + result = (*d3dintf->device.set_vertex_shader)(d3d->device, VERTEX_FORMAT); + if (result != D3D_OK) + { + mame_printf_error("Error setting vertex shader (%08X)", (UINT32)result); + return 1; + } + + // set the fixed render state + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ZENABLE, D3DZB_FALSE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_FILLMODE, D3DFILL_SOLID); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_SHADEMODE, D3DSHADE_FLAT); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ZWRITEENABLE, FALSE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHATESTENABLE, TRUE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_LASTPIXEL, TRUE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_CULLMODE, D3DCULL_NONE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ZFUNC, D3DCMP_LESS); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHAREF, 0); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHAFUNC, D3DCMP_GREATER); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_DITHERENABLE, FALSE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_FOGENABLE, FALSE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_SPECULARENABLE, FALSE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_STENCILENABLE, FALSE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_WRAP0, FALSE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_CLIPPING, TRUE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_LIGHTING, FALSE); + result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_COLORVERTEX, TRUE); + + result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_COLOROP, D3DTOP_MODULATE); + result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + + // reset the local states to force updates + reset_render_states(d3d); + + // clear the buffer + result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0); + result = (*d3dintf->device.present)(d3d->device, NULL, NULL, NULL, NULL, 0); + + // experimental: if we have a vector bitmap, create a texture for it + if (d3d->vector_bitmap != NULL) + { + render_texinfo texture; + + // fake in the basic data so it looks like it came from render.c + texture.base = d3d->vector_bitmap->base; + texture.rowpixels = d3d->vector_bitmap->rowpixels; + texture.width = d3d->vector_bitmap->width; + texture.height = d3d->vector_bitmap->height; + texture.palette = NULL; + texture.seqid = 0; + + // now create it + d3d->vector_texture = texture_create(d3d, &texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32)); + } + + return 0; +} + + + +//============================================================ +// device_delete +//============================================================ + +static void device_delete(d3d_info *d3d) +{ + // free resources + device_delete_resources(d3d); + + // free the device itself + if (d3d->device != NULL) + (*d3dintf->device.release)(d3d->device); + d3d->device = NULL; +} + + + +//============================================================ +// device_delete_resources +//============================================================ + +static void device_delete_resources(d3d_info *d3d) +{ + // free all textures + while (d3d->texlist != NULL) + { + texture_info *tex = d3d->texlist; + d3d->texlist = tex->next; + if (tex->d3dfinaltex != NULL) + (*d3dintf->texture.release)(tex->d3dfinaltex); + if (tex->d3dtex != NULL && tex->d3dtex != tex->d3dfinaltex) + (*d3dintf->texture.release)(tex->d3dtex); + if (tex->d3dsurface != NULL) + (*d3dintf->surface.release)(tex->d3dsurface); + free(tex); + } + + // free the vertex buffer + if (d3d->vertexbuf != NULL) + (*d3dintf->vertexbuf.release)(d3d->vertexbuf); + d3d->vertexbuf = NULL; +} + + + +//============================================================ +// device_verify_caps +//============================================================ + +static int device_verify_caps(d3d_info *d3d) +{ + int retval = 0; + HRESULT result; + DWORD tempcaps; + + // fetch a few core caps + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_CAPS, &d3d->texture_caps); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_MAX_TEXTURE_ASPECT, &d3d->texture_max_aspect); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_MAX_TEXTURE_WIDTH, &d3d->texture_max_width); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_MAX_TEXTURE_HEIGHT, &d3d->texture_max_height); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + + // verify presentation capabilities + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_PRESENTATION_INTERVALS, &tempcaps); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + if (!(tempcaps & D3DPRESENT_INTERVAL_IMMEDIATE)) + { + mame_printf_verbose("Direct3D: Error - Device does not support immediate presentations\n"); + retval = 2; + } + if (!(tempcaps & D3DPRESENT_INTERVAL_ONE)) + { + mame_printf_verbose("Direct3D: Error - Device does not support per-refresh presentations\n"); + retval = 2; + } + + // verify device capabilities + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_DEV_CAPS, &tempcaps); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + if (!(tempcaps & D3DDEVCAPS_CANRENDERAFTERFLIP)) + { + mame_printf_verbose("Direct3D: Warning - Device does not support queued rendering after a page flip\n"); + retval = 1; + } + if (!(tempcaps & D3DDEVCAPS_HWRASTERIZATION)) + { + mame_printf_verbose("Direct3D: Warning - Device does not support hardware rasterization\n"); + retval = 1; + } + + // verify source blend capabilities + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_SRCBLEND_CAPS, &tempcaps); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + if (!(tempcaps & D3DPBLENDCAPS_SRCALPHA)) + { + mame_printf_verbose("Direct3D: Error - Device does not support source alpha blending with source alpha\n"); + retval = 2; + } + if (!(tempcaps & D3DPBLENDCAPS_DESTCOLOR)) + { + mame_printf_verbose("Direct3D: Error - Device does not support source alpha blending with destination color\n"); + retval = 2; + } + + // verify destination blend capabilities + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_DSTBLEND_CAPS, &tempcaps); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + if (!(tempcaps & D3DPBLENDCAPS_ZERO)) + { + mame_printf_verbose("Direct3D: Error - Device does not support dest alpha blending with zero\n"); + retval = 2; + } + if (!(tempcaps & D3DPBLENDCAPS_ONE)) + { + mame_printf_verbose("Direct3D: Error - Device does not support dest alpha blending with one\n"); + retval = 2; + } + if (!(tempcaps & D3DPBLENDCAPS_INVSRCALPHA)) + { + mame_printf_verbose("Direct3D: Error - Device does not support dest alpha blending with inverted source alpha\n"); + retval = 2; + } + + // verify texture capabilities + if (!(d3d->texture_caps & D3DPTEXTURECAPS_ALPHA)) + { + mame_printf_verbose("Direct3D: Error - Device does not support texture alpha\n"); + retval = 2; + } + + // verify texture filter capabilities + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_FILTER_CAPS, &tempcaps); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + if (!(tempcaps & D3DPTFILTERCAPS_MAGFPOINT)) + { + mame_printf_verbose("Direct3D: Warning - Device does not support point-sample texture filtering for magnification\n"); + retval = 1; + } + if (!(tempcaps & D3DPTFILTERCAPS_MAGFLINEAR)) + { + mame_printf_verbose("Direct3D: Warning - Device does not support bilinear texture filtering for magnification\n"); + retval = 1; + } + if (!(tempcaps & D3DPTFILTERCAPS_MINFPOINT)) + { + mame_printf_verbose("Direct3D: Warning - Device does not support point-sample texture filtering for minification\n"); + retval = 1; + } + if (!(tempcaps & D3DPTFILTERCAPS_MINFLINEAR)) + { + mame_printf_verbose("Direct3D: Warning - Device does not support bilinear texture filtering for minification\n"); + retval = 1; + } + + // verify texture addressing capabilities + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_ADDRESS_CAPS, &tempcaps); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + if (!(tempcaps & D3DPTADDRESSCAPS_CLAMP)) + { + mame_printf_verbose("Direct3D: Warning - Device does not support texture clamping\n"); + retval = 1; + } + if (!(tempcaps & D3DPTADDRESSCAPS_WRAP)) + { + mame_printf_verbose("Direct3D: Warning - Device does not support texture wrapping\n"); + retval = 1; + } + + // verify texture operation capabilities + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_OP_CAPS, &tempcaps); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + if (!(tempcaps & D3DTEXOPCAPS_MODULATE)) + { + mame_printf_verbose("Direct3D: Warning - Device does not support texture modulation\n"); + retval = 1; + } + + // set a simpler flag to indicate mod2x and mod4x texture modes + d3d->mod2x_supported = ((tempcaps & D3DTEXOPCAPS_MODULATE2X) != 0); + d3d->mod4x_supported = ((tempcaps & D3DTEXOPCAPS_MODULATE4X) != 0); + + // set a simpler flag to indicate we can use a gamma ramp + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_CAPS2, &tempcaps); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + d3d->dynamic_supported = ((tempcaps & D3DCAPS2_DYNAMICTEXTURES) != 0); + d3d->gamma_supported = ((tempcaps & D3DCAPS2_FULLSCREENGAMMA) != 0); + if (d3d->dynamic_supported) mame_printf_verbose("Direct3D: Using dynamic textures\n"); + + // set a simpler flag to indicate we can use StretchRect + result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_STRETCH_RECT_FILTER, &tempcaps); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); + d3d->stretch_supported = ((tempcaps & D3DPTFILTERCAPS_MAGFPOINT) != 0); + if (d3d->stretch_supported && video_config.prescale > 1) mame_printf_verbose("Direct3D: Using StretchRect for prescaling\n"); + + return retval; +} + + + +//============================================================ +// device_test_cooperative +//============================================================ + +static int device_test_cooperative(d3d_info *d3d) +{ + HRESULT result; + + // check our current status; if we lost the device, punt to GDI + result = (*d3dintf->device.test_cooperative_level)(d3d->device); + if (result == D3DERR_DEVICELOST) + return 1; + + // if we're able to reset ourselves, try it + if (result == D3DERR_DEVICENOTRESET) + { + mame_printf_verbose("Direct3D: resetting device\n"); + + // free all existing resources and call reset on the device + device_delete_resources(d3d); + result = (*d3dintf->device.reset)(d3d->device, &d3d->presentation); + + // if it didn't work, punt to GDI + if (result != D3D_OK) + return 1; + + // try to create the resources again; if that didn't work, delete the whole thing + if (device_create_resources(d3d)) + { + mame_printf_verbose("Direct3D: failed to recreate resources for device; failing permanently\n"); + device_delete(d3d); + return 1; + } + } + return 0; +} + + + +//============================================================ +// config_adapter_mode +//============================================================ + +static int config_adapter_mode(win_window_info *window) +{ + d3d_adapter_identifier identifier; + d3d_info *d3d = window->drawdata; + HRESULT result; + + // choose the monitor number + d3d->adapter = get_adapter_for_monitor(d3d, window->monitor); + + // get the identifier + result = (*d3dintf->d3d.get_adapter_identifier)(d3dintf, d3d->adapter, 0, &identifier); + if (result != D3D_OK) + { + mame_printf_error("Error getting identifier for adapter #%d\n", d3d->adapter); + return 1; + } + mame_printf_verbose("Direct3D: Configuring adapter #%d = %s\n", d3d->adapter, identifier.Description); + + // get the current display mode + result = (*d3dintf->d3d.get_adapter_display_mode)(d3dintf, d3d->adapter, &d3d->origmode); + if (result != D3D_OK) + { + mame_printf_error("Error getting mode for adapter #%d\n", d3d->adapter); + return 1; + } + + // choose a resolution: window mode case + if (!window->fullscreen || !video_config.switchres || HAS_WINDOW_MENU) + { + RECT client; + + // bounds are from the window client rect + GetClientRect(window->hwnd, &client); + d3d->width = client.right - client.left; + d3d->height = client.bottom - client.top; + + // pix format is from the current mode + d3d->pixformat = d3d->origmode.Format; + d3d->refresh = 0; + + // make sure it's a pixel format we can get behind + if (d3d->pixformat != D3DFMT_X1R5G5B5 && d3d->pixformat != D3DFMT_R5G6B5 && d3d->pixformat != D3DFMT_X8R8G8B8) + { + char *utf8_device = utf8_from_tstring(window->monitor->info.szDevice); + if (utf8_device != NULL) + { + mame_printf_error("Device %s currently in an unsupported mode\n", utf8_device); + free(utf8_device); + } + return 1; + } + } + + // choose a resolution: full screen mode case + else + { + // default to the current mode exactly + d3d->width = d3d->origmode.Width; + d3d->height = d3d->origmode.Height; + d3d->pixformat = d3d->origmode.Format; + d3d->refresh = d3d->origmode.RefreshRate; + + // if we're allowed to switch resolutions, override with something better + if (video_config.switchres) + pick_best_mode(window); + } + + // see if we can handle the device type + result = (*d3dintf->d3d.check_device_type)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, d3d->pixformat, !window->fullscreen); + if (result != D3D_OK) + { + char *utf8_device = utf8_from_tstring(window->monitor->info.szDevice); + if (utf8_device != NULL) + { + mame_printf_error("Proposed video mode not supported on device %s\n", utf8_device); + free(utf8_device); + } + return 1; + } + return 0; +} + + + +//============================================================ +// get_adapter_for_monitor +//============================================================ + +static int get_adapter_for_monitor(d3d_info *d3d, win_monitor_info *monitor) +{ + int maxadapter = (*d3dintf->d3d.get_adapter_count)(d3dintf); + int adapternum; + + // iterate over adapters until we error or find a match + for (adapternum = 0; adapternum < maxadapter; adapternum++) + { + HMONITOR curmonitor; + + // get the monitor for this adapter + curmonitor = (*d3dintf->d3d.get_adapter_monitor)(d3dintf, adapternum); + + // if we match the proposed monitor, this is it + if (curmonitor == monitor->handle) + return adapternum; + } + + // default to the default + return D3DADAPTER_DEFAULT; +} + + + +//============================================================ +// pick_best_mode +//============================================================ + +static void pick_best_mode(win_window_info *window) +{ + double target_refresh = ATTOSECONDS_TO_HZ(Machine->screen[0].refresh); + INT32 target_width, target_height; + d3d_info *d3d = window->drawdata; + INT32 minwidth, minheight; + float best_score = 0.0; + int maxmodes; + int modenum; + + // determine the minimum width/height for the selected target + // note: technically we should not be calling this from an alternate window + // thread; however, it is only done during init time, and the init code on + // the main thread is waiting for us to finish, so it is safe to do so here + render_target_get_minimum_size(window->target, &minwidth, &minheight); + + // use those as the target for now + target_width = minwidth; + target_height = minheight; + + // determine the maximum number of modes + maxmodes = (*d3dintf->d3d.get_adapter_mode_count)(d3dintf, d3d->adapter, D3DFMT_X8R8G8B8); + + // enumerate all the video modes and find the best match + mame_printf_verbose("Direct3D: Selecting video mode...\n"); + for (modenum = 0; modenum < maxmodes; modenum++) + { + float size_score, refresh_score, final_score; + D3DDISPLAYMODE mode; + HRESULT result; + + // check this mode + result = (*d3dintf->d3d.enum_adapter_modes)(d3dintf, d3d->adapter, D3DFMT_X8R8G8B8, modenum, &mode); + if (result != D3D_OK) + break; + + // skip non-32 bit modes + if (mode.Format != D3DFMT_X8R8G8B8) + continue; + + // compute initial score based on difference between target and current + size_score = 1.0f / (1.0f + fabs(mode.Width - target_width) + fabs(mode.Height - target_height)); + + // if the mode is too small, give a big penalty + if (mode.Width < minwidth || mode.Height < minheight) + size_score *= 0.01f; + + // if mode is smaller than we'd like, it only scores up to 0.1 + if (mode.Width < target_width || mode.Height < target_height) + size_score *= 0.1f; + + // if we're looking for a particular mode, that's a winner + if (mode.Width == window->maxwidth && mode.Height == window->maxheight) + size_score = 2.0f; + + // compute refresh score + refresh_score = 1.0f / (1.0f + fabs((double)mode.RefreshRate - target_refresh)); + + // if refresh is smaller than we'd like, it only scores up to 0.1 + if ((double)mode.RefreshRate < target_refresh) + refresh_score *= 0.1; + + // if we're looking for a particular refresh, make sure it matches + if (mode.RefreshRate == window->refresh) + refresh_score = 2.0f; + + // weight size and refresh equally + final_score = size_score + refresh_score; + + // best so far? + mame_printf_verbose(" %4dx%4d@%3dHz -> %f\n", mode.Width, mode.Height, mode.RefreshRate, final_score * 1000.0f); + if (final_score > best_score) + { + best_score = final_score; + d3d->width = mode.Width; + d3d->height = mode.Height; + d3d->pixformat = mode.Format; + d3d->refresh = mode.RefreshRate; + } + } + mame_printf_verbose("Direct3D: Mode selected = %4dx%4d@%3dHz\n", d3d->width, d3d->height, d3d->refresh); +} + + + +//============================================================ +// update_window_size +//============================================================ + +static int update_window_size(win_window_info *window) +{ + d3d_info *d3d = window->drawdata; + RECT client; + + // get the current window bounds + GetClientRect(window->hwnd, &client); + + // if we have a device and matching width/height, nothing to do + if (d3d->device != NULL && rect_width(&client) == d3d->width && rect_height(&client) == d3d->height) + { + // clear out any pending resizing if the area didn't change + if (window->resize_state == RESIZE_STATE_PENDING) + window->resize_state = RESIZE_STATE_NORMAL; + return FALSE; + } + + // if we're in the middle of resizing, leave it alone as well + if (window->resize_state == RESIZE_STATE_RESIZING) + return FALSE; + + // set the new bounds and create the device again + d3d->width = rect_width(&client); + d3d->height = rect_height(&client); + if (device_create(window)) + return FALSE; + + // reset the resize state to normal, and indicate we made a change + window->resize_state = RESIZE_STATE_NORMAL; + return TRUE; +} + + + +//============================================================ +// draw_line +//============================================================ + +static void draw_line(d3d_info *d3d, const render_primitive *prim) +{ + const line_aa_step *step = line_aa_4step; + render_bounds b0, b1; + d3d_vertex *vertex; + INT32 r, g, b, a; + poly_info *poly; + float effwidth; + DWORD color; + int i; + + // compute the effective width based on the direction of the line + effwidth = prim->width; + if (effwidth < 0.5f) + effwidth = 0.5f; + + // determine the bounds of a quad to draw this line + render_line_to_quad(&prim->bounds, effwidth, &b0, &b1); + + // iterate over AA steps + for (step = PRIMFLAG_GET_ANTIALIAS(prim->flags) ? line_aa_4step : line_aa_1step; step->weight != 0; step++) + { + // get a pointer to the vertex buffer + vertex = primitive_alloc(d3d, 4); + if (vertex == NULL) + return; + + // rotate the unit vector by 135 degrees and add to point 0 + vertex[0].x = b0.x0 + step->xoffs; + vertex[0].y = b0.y0 + step->yoffs; + + // rotate the unit vector by -135 degrees and add to point 0 + vertex[1].x = b0.x1 + step->xoffs; + vertex[1].y = b0.y1 + step->yoffs; + + // rotate the unit vector by 45 degrees and add to point 1 + vertex[2].x = b1.x0 + step->xoffs; + vertex[2].y = b1.y0 + step->yoffs; + + // rotate the unit vector by -45 degrees and add to point 1 + vertex[3].x = b1.x1 + step->xoffs; + vertex[3].y = b1.y1 + step->yoffs; + + // determine the color of the line + r = (INT32)(prim->color.r * step->weight * 255.0f); + g = (INT32)(prim->color.g * step->weight * 255.0f); + b = (INT32)(prim->color.b * step->weight * 255.0f); + a = (INT32)(prim->color.a * 255.0f); + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + if (a > 255) a = 255; + color = D3DCOLOR_ARGB(a, r, g, b); + + // if we have a texture to use for the vectors, use it here + if (d3d->vector_texture != NULL) + { + vertex[0].u0 = d3d->vector_texture->ustart; + vertex[0].v0 = d3d->vector_texture->vstart; + + vertex[2].u0 = d3d->vector_texture->ustop; + vertex[2].v0 = d3d->vector_texture->vstart; + + vertex[1].u0 = d3d->vector_texture->ustart; + vertex[1].v0 = d3d->vector_texture->vstop; + + vertex[3].u0 = d3d->vector_texture->ustop; + vertex[3].v0 = d3d->vector_texture->vstop; + } + + // set the color, Z parameters to standard values + for (i = 0; i < 4; i++) + { + vertex[i].z = 0.0f; + vertex[i].rhw = 1.0f; + vertex[i].color = color; + } + + // now add a polygon entry + poly = &d3d->poly[d3d->numpolys++]; + poly->type = D3DPT_TRIANGLESTRIP; + poly->count = 2; + poly->numverts = 4; + poly->flags = prim->flags; + poly->modmode = D3DTOP_MODULATE; + poly->texture = d3d->vector_texture; + } +} + + + +//============================================================ +// draw_quad +//============================================================ + +static void draw_quad(d3d_info *d3d, const render_primitive *prim) +{ + texture_info *texture = texture_find(d3d, prim); + DWORD color, modmode; + d3d_vertex *vertex; + INT32 r, g, b, a; + poly_info *poly; + int i; + + // get a pointer to the vertex buffer + vertex = primitive_alloc(d3d, 4); + if (vertex == NULL) + return; + + // fill in the vertexes clockwise + vertex[0].x = prim->bounds.x0 - 0.5f; + vertex[0].y = prim->bounds.y0 - 0.5f; + vertex[1].x = prim->bounds.x1 - 0.5f; + vertex[1].y = prim->bounds.y0 - 0.5f; + vertex[2].x = prim->bounds.x0 - 0.5f; + vertex[2].y = prim->bounds.y1 - 0.5f; + vertex[3].x = prim->bounds.x1 - 0.5f; + vertex[3].y = prim->bounds.y1 - 0.5f; + + // set the texture coordinates + if (texture != NULL) + { + float du = texture->ustop - texture->ustart; + float dv = texture->vstop - texture->vstart; + vertex[0].u0 = texture->ustart + du * prim->texcoords.tl.u; + vertex[0].v0 = texture->vstart + dv * prim->texcoords.tl.v; + vertex[1].u0 = texture->ustart + du * prim->texcoords.tr.u; + vertex[1].v0 = texture->vstart + dv * prim->texcoords.tr.v; + vertex[2].u0 = texture->ustart + du * prim->texcoords.bl.u; + vertex[2].v0 = texture->vstart + dv * prim->texcoords.bl.v; + vertex[3].u0 = texture->ustart + du * prim->texcoords.br.u; + vertex[3].v0 = texture->vstart + dv * prim->texcoords.br.v; + } + + // determine the color, allowing for over modulation + r = (INT32)(prim->color.r * 255.0f); + g = (INT32)(prim->color.g * 255.0f); + b = (INT32)(prim->color.b * 255.0f); + a = (INT32)(prim->color.a * 255.0f); + modmode = D3DTOP_MODULATE; + if (texture != NULL) + { + if (d3d->mod2x_supported && (r > 255 || g > 255 || b > 255)) + { + if (d3d->mod4x_supported && (r > 2*255 || g > 2*255 || b > 2*255)) + { + r >>= 2; g >>= 2; b >>= 2; + modmode = D3DTOP_MODULATE4X; + } + else + { + r >>= 1; g >>= 1; b >>= 1; + modmode = D3DTOP_MODULATE2X; + } + } + } + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + if (a > 255) a = 255; + color = D3DCOLOR_ARGB(a, r, g, b); + + // set the color, Z parameters to standard values + for (i = 0; i < 4; i++) + { + vertex[i].z = 0.0f; + vertex[i].rhw = 1.0f; + vertex[i].color = color; + } + + // now add a polygon entry + poly = &d3d->poly[d3d->numpolys++]; + poly->type = D3DPT_TRIANGLESTRIP; + poly->count = 2; + poly->numverts = 4; + poly->flags = prim->flags; + poly->modmode = modmode; + poly->texture = texture; +} + + + +//============================================================ +// primitive_alloc +//============================================================ + +static d3d_vertex *primitive_alloc(d3d_info *d3d, int numverts) +{ + HRESULT result; + + // if we're going to overflow, flush + if (d3d->lockedbuf != NULL && d3d->numverts + numverts >= VERTEX_BUFFER_SIZE) + primitive_flush_pending(d3d); + + // if we don't have a lock, grab it now + if (d3d->lockedbuf == NULL) + { + result = (*d3dintf->vertexbuf.lock)(d3d->vertexbuf, 0, 0, (VOID **)&d3d->lockedbuf, D3DLOCK_DISCARD); + if (result != D3D_OK) + return NULL; + } + + // if we already have the lock and enough room, just return a pointer + if (d3d->lockedbuf != NULL && d3d->numverts + numverts < VERTEX_BUFFER_SIZE) + { + int oldverts = d3d->numverts; + d3d->numverts += numverts; + return &d3d->lockedbuf[oldverts]; + } + return NULL; +} + + + +//============================================================ +// primitive_flush_pending +//============================================================ + +static void primitive_flush_pending(d3d_info *d3d) +{ + HRESULT result; + int polynum; + int vertnum; + + // ignore if we're not locked + if (d3d->lockedbuf == NULL) + return; + + // unlock the buffer + result = (*d3dintf->vertexbuf.unlock)(d3d->vertexbuf); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during vertex buffer unlock call\n", (int)result); + d3d->lockedbuf = NULL; + + // set the stream + result = (*d3dintf->device.set_stream_source)(d3d->device, 0, d3d->vertexbuf, sizeof(d3d_vertex)); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_stream_source call\n", (int)result); + + // now do the polys + for (polynum = vertnum = 0; polynum < d3d->numpolys; polynum++) + { + poly_info *poly = &d3d->poly[polynum]; + int newfilter; + + // set the texture if different + set_texture(d3d, poly->texture); + + // set filtering if different + if (poly->texture != NULL) + { + newfilter = FALSE; + if (PRIMFLAG_GET_SCREENTEX(poly->flags)) + newfilter = video_config.filter; + set_filter(d3d, newfilter); + set_wrap(d3d, PRIMFLAG_GET_TEXWRAP(poly->flags)); + set_modmode(d3d, poly->modmode); + } + + // set the blendmode if different + set_blendmode(d3d, PRIMFLAG_GET_BLENDMODE(poly->flags)); + + // add the primitives + assert(vertnum + poly->numverts <= d3d->numverts); + result = (*d3dintf->device.draw_primitive)(d3d->device, poly->type, vertnum, poly->count); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device draw_primitive call\n", (int)result); + vertnum += poly->numverts; + } + + // reset the vertex count + d3d->numverts = 0; + d3d->numpolys = 0; +} + + + +//============================================================ +// texture_create +//============================================================ + +static texture_info *texture_create(d3d_info *d3d, const render_texinfo *texsource, UINT32 flags) +{ + texture_info *texture; + HRESULT result; + + // allocate a new texture + texture = malloc_or_die(sizeof(*texture)); + memset(texture, 0, sizeof(*texture)); + + // fill in the core data + texture->hash = texture_compute_hash(texsource, flags); + texture->flags = flags; + texture->texinfo = *texsource; + texture->xprescale = video_config.prescale; + texture->yprescale = video_config.prescale; + + // compute the size + texture_compute_size(d3d, texsource->width, texsource->height, texture); + + // non-screen textures are easy + if (!PRIMFLAG_GET_SCREENTEX(flags)) + { + assert(PRIMFLAG_TEXFORMAT(flags) != TEXFORMAT_YUY16); + result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture->d3dtex); + if (result != D3D_OK) + goto error; + texture->d3dfinaltex = texture->d3dtex; + texture->type = TEXTURE_TYPE_PLAIN; + } + + // screen textures are allocated differently + else + { + D3DFORMAT format; + DWORD usage = d3d->dynamic_supported ? D3DUSAGE_DYNAMIC : 0; + DWORD pool = d3d->dynamic_supported ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + int maxdim = MAX(d3d->presentation.BackBufferWidth, d3d->presentation.BackBufferHeight); + + // pick the format + if (PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_YUY16) + format = d3d->yuv_format; + else if (PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_ARGB32 || PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_PALETTEA16) + format = D3DFMT_A8R8G8B8; + else + format = d3d->screen_format; + + // don't prescale above screen size + while (texture->xprescale > 1 && texture->rawwidth * texture->xprescale >= 2 * maxdim) + texture->xprescale--; + while (texture->xprescale > 1 && texture->rawwidth * texture->xprescale > d3d->texture_max_width) + texture->xprescale--; + while (texture->yprescale > 1 && texture->rawheight * texture->yprescale >= 2 * maxdim) + texture->yprescale--; + while (texture->yprescale > 1 && texture->rawheight * texture->yprescale > d3d->texture_max_height) + texture->yprescale--; + if (texture->xprescale != video_config.prescale || texture->yprescale != video_config.prescale) + mame_printf_verbose("Direct3D: adjusting prescale from %dx%d to %dx%d\n", video_config.prescale, video_config.prescale, texture->xprescale, texture->yprescale); + + // screen textures with no prescaling are pretty easy + if (texture->xprescale == 1 && texture->yprescale == 1) + { + result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, usage, format, pool, &texture->d3dtex); + if (result != D3D_OK) + goto error; + texture->d3dfinaltex = texture->d3dtex; + texture->type = d3d->dynamic_supported ? TEXTURE_TYPE_DYNAMIC : TEXTURE_TYPE_PLAIN; + } + + // screen textures with prescaling require two allocations + else + { + int scwidth, scheight; + + // use an offscreen plain surface for stretching if supported + if (d3d->stretch_supported) + { + result = (*d3dintf->device.create_offscreen_plain_surface)(d3d->device, texture->rawwidth, texture->rawheight, format, D3DPOOL_DEFAULT, &texture->d3dsurface); + if (result != D3D_OK) + goto error; + texture->type = TEXTURE_TYPE_SURFACE; + } + + // otherwise, we allocate a dynamic texture for the source + else + { + result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, usage, format, pool, &texture->d3dtex); + if (result != D3D_OK) + goto error; + texture->type = d3d->dynamic_supported ? TEXTURE_TYPE_DYNAMIC : TEXTURE_TYPE_PLAIN; + } + + // for the target surface, we allocate a render target texture + scwidth = texture->rawwidth * texture->xprescale; + scheight = texture->rawheight * texture->yprescale; + result = (*d3dintf->device.create_texture)(d3d->device, scwidth, scheight, 1, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture->d3dfinaltex); + if (result != D3D_OK) + goto error; + } + } + + // copy the data to the texture + texture_set_data(d3d, texture, texsource, flags); + + // add us to the texture list + texture->next = d3d->texlist; + d3d->texlist = texture; + return texture; + +error: + if (texture->d3dsurface != NULL) + (*d3dintf->surface.release)(texture->d3dsurface); + if (texture->d3dtex != NULL) + (*d3dintf->texture.release)(texture->d3dtex); + free(texture); + return NULL; +} + + + +//============================================================ +// texture_compute_size +//============================================================ + +static void texture_compute_size(d3d_info *d3d, int texwidth, int texheight, texture_info *texture) +{ + int finalheight = texheight; + int finalwidth = texwidth; + + // if we're not wrapping, add a 1 pixel border on all sides +#if ENABLE_BORDER_PIX + texture->borderpix = !(texture->flags & PRIMFLAG_TEXWRAP_MASK); +#else + texture->borderpix = FALSE; +#endif + if (texture->borderpix) + { + finalwidth += 2; + finalheight += 2; + } + + // round width/height up to nearest power of 2 if we need to + if (!(d3d->texture_caps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) + { + // first the width + if (finalwidth & (finalwidth - 1)) + { + finalwidth |= finalwidth >> 1; + finalwidth |= finalwidth >> 2; + finalwidth |= finalwidth >> 4; + finalwidth |= finalwidth >> 8; + finalwidth++; + } + + // then the height + if (finalheight & (finalheight - 1)) + { + finalheight |= finalheight >> 1; + finalheight |= finalheight >> 2; + finalheight |= finalheight >> 4; + finalheight |= finalheight >> 8; + finalheight++; + } + } + + // round up to square if we need to + if (d3d->texture_caps & D3DPTEXTURECAPS_SQUAREONLY) + { + if (finalwidth < finalheight) + finalwidth = finalheight; + else + finalheight = finalwidth; + } + + // adjust the aspect ratio if we need to + while (finalwidth < finalheight && finalheight / finalwidth > d3d->texture_max_aspect) + finalwidth *= 2; + while (finalheight < finalwidth && finalwidth / finalheight > d3d->texture_max_aspect) + finalheight *= 2; + + // if we added pixels for the border, and that just barely pushed us over, take it back + if (texture->borderpix && + ((finalwidth > d3d->texture_max_width && finalwidth - 2 <= d3d->texture_max_width) || + (finalheight > d3d->texture_max_height && finalheight - 2 <= d3d->texture_max_height))) + { + texture->borderpix = FALSE; + finalwidth -= 2; + finalheight -= 2; + } + + // if we're above the max width/height, do what? + if (finalwidth > d3d->texture_max_width || finalheight > d3d->texture_max_height) + { + static int printed = FALSE; + if (!printed) mame_printf_warning("Texture too big! (wanted: %dx%d, max is %dx%d)\n", finalwidth, finalheight, (int)d3d->texture_max_width, (int)d3d->texture_max_height); + printed = TRUE; + } + + // compute the U/V scale factors + if (texture->borderpix) + { + texture->ustart = 1.0f / (float)finalwidth; + texture->ustop = (float)(texwidth + 1) / (float)finalwidth; + texture->vstart = 1.0f / (float)finalheight; + texture->vstop = (float)(texheight + 1) / (float)finalheight; + } + else + { + texture->ustart = 0.0f; + texture->ustop = (float)texwidth / (float)finalwidth; + texture->vstart = 0.0f; + texture->vstop = (float)texheight / (float)finalheight; + } + + // set the final values + texture->rawwidth = finalwidth; + texture->rawheight = finalheight; +} + + + +//============================================================ +// copyline_palette16 +//============================================================ + +INLINE void copyline_palette16(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) +{ + int x; + + if (borderpix) + *dst++ = 0xff000000 | palette[*src]; + for (x = 0; x < width; x++) + *dst++ = 0xff000000 | palette[*src++]; + if (borderpix) + *dst++ = 0xff000000 | palette[*--src]; +} + + + +//============================================================ +// copyline_palettea16 +//============================================================ + +INLINE void copyline_palettea16(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) +{ + int x; + + if (borderpix) + *dst++ = palette[*src]; + for (x = 0; x < width; x++) + *dst++ = palette[*src++]; + if (borderpix) + *dst++ = palette[*--src]; +} + + + +//============================================================ +// copyline_rgb15 +//============================================================ + +INLINE void copyline_rgb15(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) +{ + int x; + + // palette (really RGB map) case + if (palette != NULL) + { + if (borderpix) + { + UINT16 pix = *src; + *dst++ = 0xff000000 | palette[0x40 + ((pix >> 10) & 0x1f)] | palette[0x20 + ((pix >> 5) & 0x1f)] | palette[0x00 + ((pix >> 0) & 0x1f)]; + } + for (x = 0; x < width; x++) + { + UINT16 pix = *src++; + *dst++ = 0xff000000 | palette[0x40 + ((pix >> 10) & 0x1f)] | palette[0x20 + ((pix >> 5) & 0x1f)] | palette[0x00 + ((pix >> 0) & 0x1f)]; + } + if (borderpix) + { + UINT16 pix = *--src; + *dst++ = 0xff000000 | palette[0x40 + ((pix >> 10) & 0x1f)] | palette[0x20 + ((pix >> 5) & 0x1f)] | palette[0x00 + ((pix >> 0) & 0x1f)]; + } + } + + // direct case + else + { + if (borderpix) + { + UINT16 pix = *src; + UINT32 color = ((pix & 0x7c00) << 9) | ((pix & 0x03e0) << 6) | ((pix & 0x001f) << 3); + *dst++ = 0xff000000 | color | ((color >> 5) & 0x070707); + } + for (x = 0; x < width; x++) + { + UINT16 pix = *src++; + UINT32 color = ((pix & 0x7c00) << 9) | ((pix & 0x03e0) << 6) | ((pix & 0x001f) << 3); + *dst++ = 0xff000000 | color | ((color >> 5) & 0x070707); + } + if (borderpix) + { + UINT16 pix = *--src; + UINT32 color = ((pix & 0x7c00) << 9) | ((pix & 0x03e0) << 6) | ((pix & 0x001f) << 3); + *dst++ = 0xff000000 | color | ((color >> 5) & 0x070707); + } + } +} + + + +//============================================================ +// copyline_rgb32 +//============================================================ + +INLINE void copyline_rgb32(UINT32 *dst, const UINT32 *src, int width, const rgb_t *palette, int borderpix) +{ + int x; + + // palette (really RGB map) case + if (palette != NULL) + { + if (borderpix) + { + UINT32 srcpix = *src; + *dst++ = 0xff000000 | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; + } + for (x = 0; x < width; x++) + { + UINT32 srcpix = *src++; + *dst++ = 0xff000000 | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; + } + if (borderpix) + { + UINT32 srcpix = *--src; + *dst++ = 0xff000000 | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; + } + } + + // direct case + else + { + if (borderpix) + *dst++ = 0xff000000 | *src; + for (x = 0; x < width; x++) + *dst++ = 0xff000000 | *src++; + if (borderpix) + *dst++ = 0xff000000 | *--src; + } +} + + + +//============================================================ +// copyline_argb32 +//============================================================ + +INLINE void copyline_argb32(UINT32 *dst, const UINT32 *src, int width, const rgb_t *palette, int borderpix) +{ + int x; + + // palette (really RGB map) case + if (palette != NULL) + { + if (borderpix) + { + UINT32 srcpix = *src; + *dst++ = (srcpix & 0xff000000) | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; + } + for (x = 0; x < width; x++) + { + UINT32 srcpix = *src++; + *dst++ = (srcpix & 0xff000000) | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; + } + if (borderpix) + { + UINT32 srcpix = *--src; + *dst++ = (srcpix & 0xff000000) | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; + } + } + + // direct case + else + { + if (borderpix) + *dst++ = *src; + for (x = 0; x < width; x++) + *dst++ = *src++; + if (borderpix) + *dst++ = *--src; + } +} + + + +//============================================================ +// copyline_yuy16_to_yuy2 +//============================================================ + +INLINE void copyline_yuy16_to_yuy2(UINT16 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) +{ + int x; + + // palette (really RGB map) case + if (palette != NULL) + { + if (borderpix) + { + UINT16 srcpix = *src; + *dst++ = palette[0x000 + (srcpix >> 8)] | (srcpix << 8); + } + for (x = 0; x < width; x++) + { + UINT16 srcpix = *src++; + *dst++ = palette[0x000 + (srcpix >> 8)] | (srcpix << 8); + } + if (borderpix) + { + UINT16 srcpix = *--src; + *dst++ = palette[0x000 + (srcpix >> 8)] | (srcpix << 8); + } + } + + // direct case + else + { + if (borderpix) + { + UINT16 srcpix = *src; + *dst++ = (srcpix >> 8) | (srcpix << 8); + } + for (x = 0; x < width; x++) + { + UINT16 srcpix = *src++; + *dst++ = (srcpix >> 8) | (srcpix << 8); + } + if (borderpix) + { + UINT16 srcpix = *--src; + *dst++ = (srcpix >> 8) | (srcpix << 8); + } + } +} + + + +//============================================================ +// copyline_yuy16_to_uyvy +//============================================================ + +INLINE void copyline_yuy16_to_uyvy(UINT16 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) +{ + int x; + + // palette (really RGB map) case + if (palette != NULL) + { + if (borderpix) + { + UINT16 srcpix = *src; + *dst++ = palette[0x100 + (srcpix >> 8)] | (srcpix & 0xff); + } + for (x = 0; x < width; x++) + { + UINT16 srcpix = *src++; + *dst++ = palette[0x100 + (srcpix >> 8)] | (srcpix & 0xff); + } + if (borderpix) + { + UINT16 srcpix = *--src; + *dst++ = palette[0x100 + (srcpix >> 8)] | (srcpix & 0xff); + } + } + + // direct case + else + { + if (borderpix) + { + UINT16 srcpix = *src; + *dst++ = (srcpix >> 8) | (srcpix << 8); + } + for (x = 0; x < width; x++) + { + UINT16 srcpix = *src++; + *dst++ = (srcpix >> 8) | (srcpix << 8); + } + if (borderpix) + { + UINT16 srcpix = *--src; + *dst++ = (srcpix >> 8) | (srcpix << 8); + } + } +} + + + +//============================================================ +// copyline_yuy16_to_argb +//============================================================ + +INLINE void copyline_yuy16_to_argb(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) +{ + int x; + + // palette (really RGB map) case + if (palette != NULL) + { + if (borderpix) + { + UINT16 srcpix0 = src[0]; + UINT16 srcpix1 = src[1]; + UINT8 cb = srcpix0 & 0xff; + UINT8 cr = srcpix1 & 0xff; + *dst++ = ycc_to_rgb(palette[0x000 + (srcpix0 >> 8)], cb, cr); + } + for (x = 0; x < width / 2; x++) + { + UINT16 srcpix0 = *src++; + UINT16 srcpix1 = *src++; + UINT8 cb = srcpix0 & 0xff; + UINT8 cr = srcpix1 & 0xff; + *dst++ = ycc_to_rgb(palette[0x000 + (srcpix0 >> 8)], cb, cr); + *dst++ = ycc_to_rgb(palette[0x000 + (srcpix1 >> 8)], cb, cr); + } + if (borderpix) + { + UINT16 srcpix1 = *--src; + UINT16 srcpix0 = *--src; + UINT8 cb = srcpix0 & 0xff; + UINT8 cr = srcpix1 & 0xff; + *dst++ = ycc_to_rgb(palette[0x000 + (srcpix1 >> 8)], cb, cr); + } + } + + // direct case + else + { + if (borderpix) + { + UINT16 srcpix0 = src[0]; + UINT16 srcpix1 = src[1]; + UINT8 cb = srcpix0 & 0xff; + UINT8 cr = srcpix1 & 0xff; + *dst++ = ycc_to_rgb(srcpix0 >> 8, cb, cr); + } + for (x = 0; x < width / 2; x++) + { + UINT16 srcpix0 = *src++; + UINT16 srcpix1 = *src++; + UINT8 cb = srcpix0 & 0xff; + UINT8 cr = srcpix1 & 0xff; + *dst++ = ycc_to_rgb(srcpix0 >> 8, cb, cr); + *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr); + } + if (borderpix) + { + UINT16 srcpix1 = *--src; + UINT16 srcpix0 = *--src; + UINT8 cb = srcpix0 & 0xff; + UINT8 cr = srcpix1 & 0xff; + *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr); + } + } +} + + + +//============================================================ +// texture_set_data +//============================================================ + +static void texture_set_data(d3d_info *d3d, texture_info *texture, const render_texinfo *texsource, UINT32 flags) +{ + D3DLOCKED_RECT rect; + HRESULT result; + int miny, maxy; + int dsty; + + // lock the texture + switch (texture->type) + { + default: + case TEXTURE_TYPE_PLAIN: result = (*d3dintf->texture.lock_rect)(texture->d3dtex, 0, &rect, NULL, 0); break; + case TEXTURE_TYPE_DYNAMIC: result = (*d3dintf->texture.lock_rect)(texture->d3dtex, 0, &rect, NULL, D3DLOCK_DISCARD); break; + case TEXTURE_TYPE_SURFACE: result = (*d3dintf->surface.lock_rect)(texture->d3dsurface, &rect, NULL, D3DLOCK_DISCARD); break; + } + if (result != D3D_OK) + return; + + // loop over Y + miny = 0 - texture->borderpix; + maxy = texsource->height + texture->borderpix; + for (dsty = miny; dsty < maxy; dsty++) + { + int srcy = (dsty < 0) ? 0 : (dsty >= texsource->height) ? texsource->height - 1 : dsty; + void *dst = (BYTE *)rect.pBits + (dsty + texture->borderpix) * rect.Pitch; + + // switch off of the format and + switch (PRIMFLAG_GET_TEXFORMAT(flags)) + { + case TEXFORMAT_PALETTE16: + copyline_palette16(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); + break; + + case TEXFORMAT_PALETTEA16: + copyline_palettea16(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); + break; + + case TEXFORMAT_RGB15: + copyline_rgb15(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); + break; + + case TEXFORMAT_RGB32: + copyline_rgb32(dst, (UINT32 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); + break; + + case TEXFORMAT_ARGB32: + copyline_argb32(dst, (UINT32 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); + break; + + case TEXFORMAT_YUY16: + if (d3d->yuv_format == D3DFMT_YUY2) + copyline_yuy16_to_yuy2(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); + else if (d3d->yuv_format == D3DFMT_UYVY) + copyline_yuy16_to_uyvy(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); + else + copyline_yuy16_to_argb(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); + break; + + default: + mame_printf_error("Unknown texture blendmode=%d format=%d\n", PRIMFLAG_GET_BLENDMODE(flags), PRIMFLAG_GET_TEXFORMAT(flags)); + break; + } + } + + // unlock + switch (texture->type) + { + default: + case TEXTURE_TYPE_PLAIN: result = (*d3dintf->texture.unlock_rect)(texture->d3dtex, 0); break; + case TEXTURE_TYPE_DYNAMIC: result = (*d3dintf->texture.unlock_rect)(texture->d3dtex, 0); break; + case TEXTURE_TYPE_SURFACE: result = (*d3dintf->surface.unlock_rect)(texture->d3dsurface); break; + } + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during texture unlock_rect call\n", (int)result); + + // prescale + texture_prescale(d3d, texture); +} + + + +//============================================================ +// texture_prescale +//============================================================ + +static void texture_prescale(d3d_info *d3d, texture_info *texture) +{ + d3d_surface *surface; + HRESULT result; + int i; + + // if we don't need to, just skip it + if (texture->d3dtex == texture->d3dfinaltex) + return; + + // for all cases, we need to get the surface of the render target + result = (*d3dintf->texture.get_surface_level)(texture->d3dfinaltex, 0, &surface); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during texture get_surface_level call\n", (int)result); + + // if we have an offscreen plain surface, we can just StretchRect to it + if (texture->type == TEXTURE_TYPE_SURFACE) + { + RECT source, dest; + + assert(texture->d3dsurface != NULL); + + // set the source bounds + source.left = source.top = 0; + source.right = texture->texinfo.width + 2 * texture->borderpix; + source.bottom = texture->texinfo.height + 2 * texture->borderpix; + + // set the target bounds + dest = source; + dest.right *= texture->xprescale; + dest.bottom *= texture->yprescale; + + // do the stretchrect + result = (*d3dintf->device.stretch_rect)(d3d->device, texture->d3dsurface, &source, surface, &dest, D3DTEXF_POINT); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device stretct_rect call\n", (int)result); + } + + // if we are using a texture render target, we need to do more preparations + else + { + d3d_surface *backbuffer; + + assert(texture->d3dtex != NULL); + + // first remember the original render target and set the new one + result = (*d3dintf->device.get_render_target)(d3d->device, 0, &backbuffer); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device get_render_target call\n", (int)result); + result = (*d3dintf->device.set_render_target)(d3d->device, 0, surface); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_target call\n", (int)result); + reset_render_states(d3d); + + // start the scene + result = (*d3dintf->device.begin_scene)(d3d->device); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device begin_scene call\n", (int)result); + + // configure the rendering pipeline + set_filter(d3d, FALSE); + set_blendmode(d3d, BLENDMODE_NONE); + result = (*d3dintf->device.set_texture)(d3d->device, 0, texture->d3dtex); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture call\n", (int)result); + + // lock the vertex buffer + result = (*d3dintf->vertexbuf.lock)(d3d->vertexbuf, 0, 0, (VOID **)&d3d->lockedbuf, D3DLOCK_DISCARD); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during vertex buffer lock call\n", (int)result); + + // configure the X/Y coordinates on the target surface + d3d->lockedbuf[0].x = -0.5f; + d3d->lockedbuf[0].y = -0.5f; + d3d->lockedbuf[1].x = (float)((texture->texinfo.width + 2 * texture->borderpix) * texture->xprescale) - 0.5f; + d3d->lockedbuf[1].y = -0.5f; + d3d->lockedbuf[2].x = -0.5f; + d3d->lockedbuf[2].y = (float)((texture->texinfo.height + 2 * texture->borderpix) * texture->yprescale) - 0.5f; + d3d->lockedbuf[3].x = (float)((texture->texinfo.width + 2 * texture->borderpix) * texture->xprescale) - 0.5f; + d3d->lockedbuf[3].y = (float)((texture->texinfo.height + 2 * texture->borderpix) * texture->yprescale) - 0.5f; + + // configure the U/V coordintes on the source texture + d3d->lockedbuf[0].u0 = 0.0f; + d3d->lockedbuf[0].v0 = 0.0f; + d3d->lockedbuf[1].u0 = (float)(texture->texinfo.width + 2 * texture->borderpix) / (float)texture->rawwidth; + d3d->lockedbuf[1].v0 = 0.0f; + d3d->lockedbuf[2].u0 = 0.0f; + d3d->lockedbuf[2].v0 = (float)(texture->texinfo.height + 2 * texture->borderpix) / (float)texture->rawheight; + d3d->lockedbuf[3].u0 = (float)(texture->texinfo.width + 2 * texture->borderpix) / (float)texture->rawwidth; + d3d->lockedbuf[3].v0 = (float)(texture->texinfo.height + 2 * texture->borderpix) / (float)texture->rawheight; + + // reset the remaining vertex parameters + for (i = 0; i < 4; i++) + { + d3d->lockedbuf[i].z = 0.0f; + d3d->lockedbuf[i].rhw = 1.0f; + d3d->lockedbuf[i].color = D3DCOLOR_ARGB(0xff,0xff,0xff,0xff); + } + + // unlock the vertex buffer + result = (*d3dintf->vertexbuf.unlock)(d3d->vertexbuf); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during vertex buffer unlock call\n", (int)result); + d3d->lockedbuf = NULL; + + // set the stream and draw the triangle strip + result = (*d3dintf->device.set_stream_source)(d3d->device, 0, d3d->vertexbuf, sizeof(d3d_vertex)); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_stream_source call\n", (int)result); + result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLESTRIP, 0, 2); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device draw_primitive call\n", (int)result); + + // end the scene + result = (*d3dintf->device.end_scene)(d3d->device); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device end_scene call\n", (int)result); + + // reset the render target and release our reference to the backbuffer + result = (*d3dintf->device.set_render_target)(d3d->device, 0, backbuffer); + if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_target call\n", (int)result); + (*d3dintf->surface.release)(backbuffer); + reset_render_states(d3d); + } + + // release our reference to the target surface + (*d3dintf->surface.release)(surface); +} + + + +//============================================================ +// texture_find +//============================================================ + +static texture_info *texture_find(d3d_info *d3d, const render_primitive *prim) +{ + UINT32 texhash = texture_compute_hash(&prim->texture, prim->flags); + texture_info *texture; + + // find a match + for (texture = d3d->texlist; texture != NULL; texture = texture->next) + if (texture->hash == texhash && + texture->texinfo.base == prim->texture.base && + texture->texinfo.width == prim->texture.width && + texture->texinfo.height == prim->texture.height && + ((texture->flags ^ prim->flags) & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)) == 0) + return texture; + + // nothing found + return NULL; +} + + + +//============================================================ +// texture_update +//============================================================ + +static void texture_update(d3d_info *d3d, const render_primitive *prim) +{ + texture_info *texture = texture_find(d3d, prim); + + // if we didn't find one, create a new texture + if (texture == NULL) + texture = texture_create(d3d, &prim->texture, prim->flags); + + // if we found it, but with a different seqid, copy the data + if (texture->texinfo.seqid != prim->texture.seqid) + { + texture_set_data(d3d, texture, &prim->texture, prim->flags); + texture->texinfo.seqid = prim->texture.seqid; + } +} diff --git a/src/osd/windows/drawdd.c b/src/osd/windows/drawdd.c new file mode 100644 index 00000000000..19bbaaee24b --- /dev/null +++ b/src/osd/windows/drawdd.c @@ -0,0 +1,1435 @@ +//============================================================ +// +// drawdd.c - Win32 DirectDraw implementation +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// needed for multimonitor +#define _WIN32_WINNT 0x501 + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +// standard C headers +#include + +// MAME headers +#include "render.h" +#include "rendutil.h" +#include "options.h" + +// MAMEOS headers +#include "winmain.h" +#include "window.h" +#include "config.h" + + + +//============================================================ +// DEBUGGING +//============================================================ + + + +//============================================================ +// CONSTANTS +//============================================================ + + + +//============================================================ +// MACROS +//============================================================ + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef HRESULT (WINAPI *directdrawcreateex_ptr)(GUID FAR *lpGuid, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter); +typedef HRESULT (WINAPI *directdrawenumerateex_ptr)(LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags); + + +/* dd_info is the information about DirectDraw for the current screen */ +typedef struct _dd_info dd_info; +struct _dd_info +{ + GUID adapter; // current display adapter + GUID * adapter_ptr; // pointer to current display adapter + int width, height; // current width, height + int refresh; // current refresh rate + int clearouter; // clear the outer areas? + + INT32 blitwidth, blitheight; // current blit width/height values + RECT lastdest; // last destination rectangle + + IDirectDraw7 * ddraw; // pointer to the DirectDraw object + IDirectDrawSurface7 * primary; // pointer to the primary surface object + IDirectDrawSurface7 * back; // pointer to the back buffer surface object + IDirectDrawSurface7 * blit; // pointer to the blit surface object + IDirectDrawClipper * clipper; // pointer to the clipper object + IDirectDrawGammaControl *gamma; // pointer to the gamma control object + + DDSURFACEDESC2 primarydesc; // description of the primary surface + DDSURFACEDESC2 blitdesc; // description of the blitting surface + DDSURFACEDESC2 origmode; // original video mode + + DDCAPS ddcaps; // capabilities of the device + DDCAPS helcaps; // capabilities of the hardware + + void * membuffer; // memory buffer for complex rendering + UINT32 membuffersize; // current size of the memory buffer +}; + + +/* monitor_enum_info holds information during a monitor enumeration */ +typedef struct _monitor_enum_info monitor_enum_info; +struct _monitor_enum_info +{ + win_monitor_info * monitor; // pointer to monitor we want + GUID guid; // GUID of the one we found + GUID * guid_ptr; // pointer to our GUID + int foundit; // TRUE if we found what we wanted +}; + + +/* mode_enum_info holds information during a display mode enumeration */ +typedef struct _mode_enum_info mode_enum_info; +struct _mode_enum_info +{ + win_window_info * window; + INT32 minimum_width, minimum_height; + INT32 target_width, target_height; + double target_refresh; + float best_score; +}; + + + +//============================================================ +// GLOBALS +//============================================================ + +static HINSTANCE dllhandle; +static directdrawcreateex_ptr directdrawcreateex; +static directdrawenumerateex_ptr directdrawenumerateex; + + + +//============================================================ +// INLINES +//============================================================ + +INLINE void update_outer_rects(dd_info *dd) +{ + dd->clearouter = (dd->back != NULL) ? 3 : 1; +} + + +INLINE int better_mode(int width0, int height0, int width1, int height1, float desired_aspect) +{ + float aspect0 = (float)width0 / (float)height0; + float aspect1 = (float)width1 / (float)height1; + return (fabs(desired_aspect - aspect0) < fabs(desired_aspect - aspect1)) ? 0 : 1; +} + + + +//============================================================ +// PROTOTYPES +//============================================================ + +// core functions +static void drawdd_exit(void); +static int drawdd_window_init(win_window_info *window); +static void drawdd_window_destroy(win_window_info *window); +static const render_primitive_list *drawdd_window_get_primitives(win_window_info *window); +static int drawdd_window_draw(win_window_info *window, HDC dc, int update); + +// surface management +static int ddraw_create(win_window_info *window); +static int ddraw_create_surfaces(win_window_info *window); +static void ddraw_delete(win_window_info *window); +static void ddraw_delete_surfaces(win_window_info *window); +static int ddraw_verify_caps(dd_info *dd); +static int ddraw_test_cooperative(win_window_info *window); +static HRESULT create_surface(dd_info *dd, DDSURFACEDESC2 *desc, IDirectDrawSurface7 **surface, const char *type); +static int create_clipper(win_window_info *window); + +// drawing helpers +static void compute_blit_surface_size(win_window_info *window); +static void blit_to_primary(win_window_info *window, int srcwidth, int srcheight); + +// video modes +static int config_adapter_mode(win_window_info *window); +static void get_adapter_for_monitor(dd_info *dd, win_monitor_info *monitor); +static void pick_best_mode(win_window_info *window); + +// rendering +static void drawdd_rgb888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_bgr888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_rgb565_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_rgb555_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_rgb888_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_bgr888_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_rgb565_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_rgb555_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); + + + +//============================================================ +// drawdd_init +//============================================================ + +int drawdd_init(win_draw_callbacks *callbacks) +{ + // dynamically grab the create function from ddraw.dll + dllhandle = LoadLibrary(TEXT("ddraw.dll")); + if (dllhandle == NULL) + { + mame_printf_verbose("DirectDraw: Unable to access ddraw.dll\n"); + return 1; + } + + // import the create function + directdrawcreateex = (directdrawcreateex_ptr)GetProcAddress(dllhandle, "DirectDrawCreateEx"); + if (directdrawcreateex == NULL) + { + mame_printf_verbose("DirectDraw: Unable to find DirectDrawCreateEx\n"); + FreeLibrary(dllhandle); + dllhandle = NULL; + return 1; + } + + // import the enumerate function + directdrawenumerateex = (directdrawenumerateex_ptr)GetProcAddress(dllhandle, "DirectDrawEnumerateExA"); + if (directdrawenumerateex == NULL) + { + mame_printf_verbose("DirectDraw: Unable to find DirectDrawEnumerateExA\n"); + FreeLibrary(dllhandle); + dllhandle = NULL; + return 1; + } + + // fill in the callbacks + callbacks->exit = drawdd_exit; + callbacks->window_init = drawdd_window_init; + callbacks->window_get_primitives = drawdd_window_get_primitives; + callbacks->window_draw = drawdd_window_draw; + callbacks->window_destroy = drawdd_window_destroy; + + mame_printf_verbose("DirectDraw: Using DirectDraw 7\n"); + return 0; +} + + + +//============================================================ +// drawdd_exit +//============================================================ + +static void drawdd_exit(void) +{ + if (dllhandle != NULL) + FreeLibrary(dllhandle); +} + + + +//============================================================ +// drawdd_window_init +//============================================================ + +static int drawdd_window_init(win_window_info *window) +{ + dd_info *dd; + + // allocate memory for our structures + dd = malloc_or_die(sizeof(*dd)); + memset(dd, 0, sizeof(*dd)); + window->drawdata = dd; + + // configure the adapter for the mode we want + if (config_adapter_mode(window)) + goto error; + + // create the ddraw object + if (ddraw_create(window)) + goto error; + + return 0; + +error: + drawdd_window_destroy(window); + mame_printf_error("Unable to initialize DirectDraw.\n"); + return 1; +} + + + +//============================================================ +// drawdd_window_destroy +//============================================================ + +static void drawdd_window_destroy(win_window_info *window) +{ + dd_info *dd = window->drawdata; + + // skip if nothing + if (dd == NULL) + return; + + // delete the ddraw object + ddraw_delete(window); + + // free the memory in the window + free(dd); + window->drawdata = NULL; +} + + + +//============================================================ +// drawdd_window_get_primitives +//============================================================ + +static const render_primitive_list *drawdd_window_get_primitives(win_window_info *window) +{ + dd_info *dd = window->drawdata; + + compute_blit_surface_size(window); + render_target_set_bounds(window->target, dd->blitwidth, dd->blitheight, 0); + render_target_set_max_update_rate(window->target, (dd->refresh == 0) ? dd->origmode.dwRefreshRate : dd->refresh); + + return render_target_get_primitives(window->target); +} + + + +//============================================================ +// drawdd_window_draw +//============================================================ + +static int drawdd_window_draw(win_window_info *window, HDC dc, int update) +{ + dd_info *dd = window->drawdata; + const render_primitive *prim; + int usemembuffer = FALSE; + HRESULT result; + + // if we haven't been created, just punt + if (dd == NULL) + return 1; + + // if we're updating, remember to erase the outer stuff + if (update) + update_outer_rects(dd); + + // if we have a ddraw object, check the cooperative level + if (ddraw_test_cooperative(window)) + return 1; + + // get the size; if we're too small, delete the existing surfaces + if (dd->blitwidth > dd->blitdesc.dwWidth || dd->blitheight > dd->blitdesc.dwHeight) + ddraw_delete_surfaces(window); + + // if we need to create surfaces, do it now + if (dd->blit == NULL && ddraw_create_surfaces(window) != 0) + return 1; + + // select our surface and lock it + result = IDirectDrawSurface7_Lock(dd->blit, NULL, &dd->blitdesc, DDLOCK_WAIT, NULL); + if (result == DDERR_SURFACELOST) + { + mame_printf_verbose("DirectDraw: Lost surfaces; deleting and retrying next frame\n"); + ddraw_delete_surfaces(window); + return 1; + } + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X locking blit surface\n", (int)result); + return 1; + } + + // render to it + osd_lock_acquire(window->primlist->lock); + + // scan the list of primitives for tricky stuff + for (prim = window->primlist->head; prim != NULL; prim = prim->next) + if (PRIMFLAG_GET_BLENDMODE(prim->flags) != BLENDMODE_NONE || + (prim->texture.base != NULL && PRIMFLAG_GET_TEXFORMAT(prim->flags) == TEXFORMAT_ARGB32)) + { + usemembuffer = TRUE; + break; + } + + // if we're using the memory buffer, draw offscreen first and then copy + if (usemembuffer) + { + int x, y; + + // based on the target format, use one of our standard renderers + switch (dd->blitdesc.ddpfPixelFormat.dwRBitMask) + { + case 0x00ff0000: drawdd_rgb888_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break; + case 0x000000ff: drawdd_bgr888_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break; + case 0xf800: drawdd_rgb565_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break; + case 0x7c00: drawdd_rgb555_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break; + default: + mame_printf_verbose("DirectDraw: Unknown target mode: R=%08X G=%08X B=%08X\n", (int)dd->blitdesc.ddpfPixelFormat.dwRBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwGBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwBBitMask); + break; + } + + // handle copying to both 16bpp and 32bpp destinations + for (y = 0; y < dd->blitheight; y++) + { + if (dd->blitdesc.ddpfPixelFormat.dwRGBBitCount == 32) + { + UINT32 *src = (UINT32 *)dd->membuffer + y * dd->blitwidth; + UINT32 *dst = (UINT32 *)((UINT8 *)dd->blitdesc.lpSurface + y * dd->blitdesc.lPitch); + for (x = 0; x < dd->blitwidth; x++) + *dst++ = *src++; + } + else if (dd->blitdesc.ddpfPixelFormat.dwRGBBitCount == 16) + { + UINT16 *src = (UINT16 *)dd->membuffer + y * dd->blitwidth; + UINT16 *dst = (UINT16 *)((UINT8 *)dd->blitdesc.lpSurface + y * dd->blitdesc.lPitch); + for (x = 0; x < dd->blitwidth; x++) + *dst++ = *src++; + } + } + + } + + // otherwise, draw directly + else + { + // based on the target format, use one of our standard renderers + switch (dd->blitdesc.ddpfPixelFormat.dwRBitMask) + { + case 0x00ff0000: drawdd_rgb888_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 4); break; + case 0x000000ff: drawdd_bgr888_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 4); break; + case 0xf800: drawdd_rgb565_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 2); break; + case 0x7c00: drawdd_rgb555_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 2); break; + default: + mame_printf_verbose("DirectDraw: Unknown target mode: R=%08X G=%08X B=%08X\n", (int)dd->blitdesc.ddpfPixelFormat.dwRBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwGBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwBBitMask); + break; + } + } + osd_lock_release(window->primlist->lock); + + // unlock and blit + result = IDirectDrawSurface7_Unlock(dd->blit, NULL); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X unlocking blit surface\n", (int)result); + + // sync to VBLANK + if ((video_config.waitvsync || video_config.syncrefresh) && video_get_throttle() && (!window->fullscreen || dd->back == NULL)) + { + result = IDirectDraw7_WaitForVerticalBlank(dd->ddraw, DDWAITVB_BLOCKBEGIN, NULL); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X waiting for VBLANK\n", (int)result); + } + + // complete the blitting + blit_to_primary(window, dd->blitwidth, dd->blitheight); + return 0; +} + + + +//============================================================ +// ddraw_create +//============================================================ + +static int ddraw_create(win_window_info *window) +{ + dd_info *dd = window->drawdata; + HRESULT result; + int verify; + + // if a device exists, free it + if (dd->ddraw != NULL) + ddraw_delete(window); + + // create the DirectDraw object + result = (*directdrawcreateex)(dd->adapter_ptr, (LPVOID *)&dd->ddraw, &IID_IDirectDraw7, NULL); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X during DirectDrawCreateEx call\n", (int)result); + goto error; + } + + // verify the caps + verify = ddraw_verify_caps(dd); + if (verify == 2) + { + mame_printf_error("DirectDraw: Error - Device does not meet minimum requirements for DirectDraw rendering\n"); + goto error; + } + if (verify == 1) + mame_printf_verbose("DirectDraw: Warning - Device may not perform well for DirectDraw rendering\n"); + + // set the cooperative level + // for non-window modes, we will use full screen here + result = IDirectDraw7_SetCooperativeLevel(dd->ddraw, win_window_list->hwnd, DDSCL_SETFOCUSWINDOW); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X during IDirectDraw7_SetCooperativeLevel(FOCUSWINDOW) call\n", (int)result); + goto error; + } + result = IDirectDraw7_SetCooperativeLevel(dd->ddraw, window->hwnd, DDSCL_SETDEVICEWINDOW | (window->fullscreen ? DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE : DDSCL_NORMAL)); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X during IDirectDraw7_SetCooperativeLevel(DEVICEWINDOW) call\n", (int)result); + goto error; + } + + // full screen mode: set the resolution + if (window->fullscreen && video_config.switchres) + { + result = IDirectDraw7_SetDisplayMode(dd->ddraw, dd->width, dd->height, 32, dd->refresh, 0); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X attempting to set video mode %dx%d@%d call\n", (int)result, dd->width, dd->height, dd->refresh); + goto error; + } + } + + return ddraw_create_surfaces(window); + +error: + ddraw_delete(window); + return 1; +} + + + +//============================================================ +// ddraw_create_surfaces +//============================================================ + +static int ddraw_create_surfaces(win_window_info *window) +{ + dd_info *dd = window->drawdata; + HRESULT result; + + // make a description of the primary surface + memset(&dd->primarydesc, 0, sizeof(dd->primarydesc)); + dd->primarydesc.dwSize = sizeof(dd->primarydesc); + dd->primarydesc.dwFlags = DDSD_CAPS; + dd->primarydesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + // for triple-buffered full screen mode, allocate flipping surfaces + if (window->fullscreen && video_config.triplebuf) + { + dd->primarydesc.dwFlags |= DDSD_BACKBUFFERCOUNT; + dd->primarydesc.ddsCaps.dwCaps |= DDSCAPS_FLIP | DDSCAPS_COMPLEX; + dd->primarydesc.dwBackBufferCount = 2; + } + + // create the primary surface and report errors + result = create_surface(dd, &dd->primarydesc, &dd->primary, "primary"); + if (result != DD_OK) goto error; + + // full screen mode: get the back surface + dd->back = NULL; + if (window->fullscreen && video_config.triplebuf) + { + DDSCAPS2 caps = { DDSCAPS_BACKBUFFER }; + result = IDirectDrawSurface7_GetAttachedSurface(dd->primary, &caps, &dd->back); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X getting attached back surface\n", (int)result); + goto error; + } + } + + // now make a description of our blit surface, based on the primary surface + if (dd->blitwidth == 0 || dd->blitheight == 0) + compute_blit_surface_size(window); + dd->blitdesc = dd->primarydesc; + dd->blitdesc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; + dd->blitdesc.dwWidth = dd->blitwidth; + dd->blitdesc.dwHeight = dd->blitheight; + dd->blitdesc.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; + + // then create the blit surface, fall back to system memory if video mem doesn't work + result = create_surface(dd, &dd->blitdesc, &dd->blit, "blit"); + if (result != DD_OK) + { + dd->blitdesc.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY; + result = create_surface(dd, &dd->blitdesc, &dd->blit, "blit"); + } + if (result != DD_OK) goto error; + + // create a memory buffer for offscreen drawing + if (dd->membuffersize < dd->blitwidth * dd->blitheight * 4) + { + dd->membuffersize = dd->blitwidth * dd->blitheight * 4; + dd->membuffer = realloc(dd->membuffer, dd->membuffersize); + } + if (dd->membuffer == NULL) + goto error; + +#ifdef MESS + // create a clipper for all modes, since MESS has dialogs + if (create_clipper(window)) + goto error; +#else + // create a clipper for windowed mode + if (!window->fullscreen && create_clipper(window)) + goto error; +#endif + + // full screen mode: set the gamma + if (window->fullscreen) + { + // only set the gamma if it's not 1.0f + float brightness = options_get_float(mame_options(), WINOPTION_FULLSCREENBRIGHTNESS); + float contrast = options_get_float(mame_options(), WINOPTION_FULLLSCREENCONTRAST); + float gamma = options_get_float(mame_options(), WINOPTION_FULLSCREENGAMMA); + if (brightness != 1.0f || contrast != 1.0f || gamma != 1.0f) + { + // see if we can get a GammaControl object + result = IDirectDrawSurface_QueryInterface(dd->primary, &IID_IDirectDrawGammaControl, (void **)&dd->gamma); + if (result != DD_OK) + { + mame_printf_warning("DirectDraw: Warning - device does not support full screen gamma correction.\n"); + dd->gamma = NULL; + } + + // proceed if we can + if (dd->gamma != NULL) + { + DDGAMMARAMP ramp; + int i; + + // create a standard ramp and set it + for (i = 0; i < 256; i++) + ramp.red[i] = ramp.green[i] = ramp.blue[i] = apply_brightness_contrast_gamma(i, brightness, contrast, gamma) << 8; + + // attempt to set it + result = IDirectDrawGammaControl_SetGammaRamp(dd->gamma, 0, &ramp); + if (result != DD_OK) + mame_printf_verbose("DirectDraw: Error %08X attempting to set gamma correction.\n", (int)result); + } + } + } + + // force some updates + update_outer_rects(dd); + return 0; + +error: + ddraw_delete_surfaces(window); + return 1; +} + + + +//============================================================ +// ddraw_delete +//============================================================ + +static void ddraw_delete(win_window_info *window) +{ + dd_info *dd = window->drawdata; + + // free surfaces + ddraw_delete_surfaces(window); + + // restore resolutions + if (dd->ddraw != NULL) + IDirectDraw7_RestoreDisplayMode(dd->ddraw); + + // reset cooperative level + if (dd->ddraw != NULL && window->hwnd != NULL) + IDirectDraw7_SetCooperativeLevel(dd->ddraw, window->hwnd, DDSCL_NORMAL); + + // release the DirectDraw object itself + if (dd->ddraw != NULL) + IDirectDraw7_Release(dd->ddraw); + dd->ddraw = NULL; +} + + + +//============================================================ +// ddraw_delete_surfaces +//============================================================ + +static void ddraw_delete_surfaces(win_window_info *window) +{ + dd_info *dd = window->drawdata; + + // release the gamma control + if (dd->gamma != NULL) + IDirectDrawGammaControl_Release(dd->gamma); + dd->gamma = NULL; + + // release the clipper + if (dd->clipper != NULL) + IDirectDrawClipper_Release(dd->clipper); + dd->clipper = NULL; + + // free the memory buffer + if (dd->membuffer != NULL) + free(dd->membuffer); + dd->membuffer = NULL; + dd->membuffersize = 0; + + // release the blit surface + if (dd->blit != NULL) + IDirectDrawSurface7_Release(dd->blit); + dd->blit = NULL; + + // release the back surface + if (dd->back != NULL) + IDirectDrawSurface7_Release(dd->back); + dd->back = NULL; + + // release the primary surface + if (dd->primary != NULL) + IDirectDrawSurface7_Release(dd->primary); + dd->primary = NULL; +} + + + +//============================================================ +// ddraw_verify_caps +//============================================================ + +static int ddraw_verify_caps(dd_info *dd) +{ + int retval = 0; + HRESULT result; + + // get the capabilities + dd->ddcaps.dwSize = sizeof(dd->ddcaps); + dd->helcaps.dwSize = sizeof(dd->helcaps); + result = IDirectDraw7_GetCaps(dd->ddraw, &dd->ddcaps, &dd->helcaps); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X during IDirectDraw7_GetCaps call\n", (int)result); + return 1; + } + + // determine if hardware stretching is available + if ((dd->ddcaps.dwCaps & DDCAPS_BLTSTRETCH) == 0) + { + mame_printf_verbose("DirectDraw: Warning - Device does not support hardware stretching\n"); + retval = 1; + } + + return retval; +} + + + +//============================================================ +// ddraw_test_cooperative +//============================================================ + +static int ddraw_test_cooperative(win_window_info *window) +{ + dd_info *dd = window->drawdata; + HRESULT result; + + // check our current status; if we lost the device, punt to GDI + result = IDirectDraw7_TestCooperativeLevel(dd->ddraw); + switch (result) + { + // punt to GDI if someone else has exclusive mode + case DDERR_NOEXCLUSIVEMODE: + case DDERR_EXCLUSIVEMODEALREADYSET: + ddraw_delete_surfaces(window); + return 1; + + // if we're ok, but we don't have a primary surface, create one + default: + case DD_OK: + if (dd->primary == NULL) + return ddraw_create_surfaces(window); + return 0; + } +} + + + +//============================================================ +// create_surface +//============================================================ + +static HRESULT create_surface(dd_info *dd, DDSURFACEDESC2 *desc, IDirectDrawSurface7 **surface, const char *type) +{ + HRESULT result; + + // create the surface as requested + result = IDirectDraw7_CreateSurface(dd->ddraw, desc, surface, NULL); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X creating %s surface\n", (int)result, type); + return result; + } + + // get a description of the primary surface + result = IDirectDrawSurface7_GetSurfaceDesc(*surface, desc); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X getting %s surface desciption\n", (int)result, type); + IDirectDrawSurface7_Release(*surface); + *surface = NULL; + return result; + } + + // print out the good stuff + mame_printf_verbose("DirectDraw: %s surface created: %dx%dx%d (R=%08X G=%08X B=%08X)\n", + type, + (int)desc->dwWidth, + (int)desc->dwHeight, + (int)desc->ddpfPixelFormat.dwRGBBitCount, + (UINT32)desc->ddpfPixelFormat.dwRBitMask, + (UINT32)desc->ddpfPixelFormat.dwGBitMask, + (UINT32)desc->ddpfPixelFormat.dwBBitMask); + return result; +} + + + +//============================================================ +// create_clipper +//============================================================ + +static int create_clipper(win_window_info *window) +{ + dd_info *dd = window->drawdata; + HRESULT result; + + // create a clipper for the primary surface + result = IDirectDraw7_CreateClipper(dd->ddraw, 0, &dd->clipper, NULL); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X creating clipper\n", (int)result); + return 1; + } + + // set the clipper's hwnd + result = IDirectDrawClipper_SetHWnd(dd->clipper, 0, window->hwnd); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X setting clipper hwnd\n", (int)result); + return 1; + } + + // set the clipper on the primary surface + result = IDirectDrawSurface7_SetClipper(dd->primary, dd->clipper); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X setting clipper on primary surface\n", (int)result); + return 1; + } + return 0; +} + + + +//============================================================ +// compute_blit_surface_size +//============================================================ + +static void compute_blit_surface_size(win_window_info *window) +{ + dd_info *dd = window->drawdata; + INT32 newwidth, newheight; + int xscale, yscale; + RECT client; + + // start with the minimum size + render_target_get_minimum_size(window->target, &newwidth, &newheight); + + // get the window's client rectangle + GetClientRect(window->hwnd, &client); + + // hardware stretch case: apply prescale + if (video_config.hwstretch) + { + int prescale = (video_config.prescale < 1) ? 1 : video_config.prescale; + + // clamp the prescale to something smaller than the target bounds + xscale = prescale; + while (xscale > 1 && newwidth * xscale > rect_width(&client)) + xscale--; + yscale = prescale; + while (yscale > 1 && newheight * yscale > rect_height(&client)) + yscale--; + } + + // non stretch case + else + { + INT32 target_width = rect_width(&client); + INT32 target_height = rect_height(&client); + float desired_aspect = 1.0f; + + // compute the appropriate visible area if we're trying to keepaspect + if (video_config.keepaspect) + { + win_monitor_info *monitor = winwindow_video_window_monitor(window, NULL); + render_target_compute_visible_area(window->target, target_width, target_height, winvideo_monitor_get_aspect(monitor), render_target_get_orientation(window->target), &target_width, &target_height); + desired_aspect = (float)target_width / (float)target_height; + } + + // compute maximum integral scaling to fit the window + xscale = (target_width + 2) / newwidth; + yscale = (target_height + 2) / newheight; + + // try a little harder to keep the aspect ratio if desired + if (video_config.keepaspect) + { + // if we could stretch more in the X direction, and that makes a better fit, bump the xscale + while (newwidth * (xscale + 1) <= rect_width(&client) && + better_mode(newwidth * xscale, newheight * yscale, newwidth * (xscale + 1), newheight * yscale, desired_aspect)) + xscale++; + + // if we could stretch more in the Y direction, and that makes a better fit, bump the yscale + while (newheight * (yscale + 1) <= rect_height(&client) && + better_mode(newwidth * xscale, newheight * yscale, newwidth * xscale, newheight * (yscale + 1), desired_aspect)) + yscale++; + + // now that we've maxed out, see if backing off the maximally stretched one makes a better fit + if (rect_width(&client) - newwidth * xscale < rect_height(&client) - newheight * yscale) + { + while (xscale > 1 && better_mode(newwidth * xscale, newheight * yscale, newwidth * (xscale - 1), newheight * yscale, desired_aspect)) + xscale--; + } + else + { + while (yscale > 1 && better_mode(newwidth * xscale, newheight * yscale, newwidth * xscale, newheight * (yscale - 1), desired_aspect)) + yscale--; + } + } + } + + // ensure at least a scale factor of 1 + if (xscale == 0) xscale = 1; + if (yscale == 0) yscale = 1; + + // apply the final scale + newwidth *= xscale; + newheight *= yscale; + if (newwidth != dd->blitwidth || newheight != dd->blitheight) + { + // force some updates + update_outer_rects(dd); + mame_printf_verbose("DirectDraw: New blit size = %dx%d\n", newwidth, newheight); + } + dd->blitwidth = newwidth; + dd->blitheight = newheight; +} + + + +//============================================================ +// calc_fullscreen_margins +//============================================================ + +static void calc_fullscreen_margins(win_window_info *window, DWORD desc_width, DWORD desc_height, RECT *margins) +{ + margins->left = 0; + margins->top = 0; + margins->right = desc_width; + margins->bottom = desc_height; + + if (win_has_menu(window)) + { + static int height_with_menubar = 0; + if (height_with_menubar == 0) + { + RECT with_menu = { 100, 100, 200, 200 }; + RECT without_menu = { 100, 100, 200, 200 }; + AdjustWindowRect(&with_menu, WS_OVERLAPPED, TRUE); + AdjustWindowRect(&without_menu, WS_OVERLAPPED, FALSE); + height_with_menubar = (with_menu.bottom - with_menu.top) - (without_menu.bottom - without_menu.top); + } + margins->top = height_with_menubar; + } +} + + + +//============================================================ +// blit_to_primary +//============================================================ + +static void blit_to_primary(win_window_info *window, int srcwidth, int srcheight) +{ + dd_info *dd = window->drawdata; + IDirectDrawSurface7 *target = (dd->back != NULL) ? dd->back : dd->primary; + win_monitor_info *monitor = winwindow_video_window_monitor(window, NULL); + DDBLTFX blitfx = { sizeof(DDBLTFX) }; + RECT clear, outer, dest, source; + INT32 dstwidth, dstheight; + HRESULT result; + + // compute source rect + source.left = source.top = 0; + source.right = srcwidth; + source.bottom = srcheight; + + // compute outer rect -- windowed version + if (!window->fullscreen) + { + GetClientRect(window->hwnd, &outer); + ClientToScreen(window->hwnd, &((LPPOINT)&outer)[0]); + ClientToScreen(window->hwnd, &((LPPOINT)&outer)[1]); + + // adjust to be relative to the monitor + outer.left -= monitor->info.rcMonitor.left; + outer.right -= monitor->info.rcMonitor.left; + outer.top -= monitor->info.rcMonitor.top; + outer.bottom -= monitor->info.rcMonitor.top; + } + + // compute outer rect -- full screen version + else + { + calc_fullscreen_margins(window, dd->primarydesc.dwWidth, dd->primarydesc.dwHeight, &outer); + } + + // if we're respecting the aspect ratio, we need to adjust to fit + dstwidth = rect_width(&outer); + dstheight = rect_height(&outer); + if (!video_config.hwstretch) + { + // trim the source if necessary + if (rect_width(&outer) < srcwidth) + { + source.left += (srcwidth - rect_width(&outer)) / 2; + source.right = source.left + rect_width(&outer); + } + if (rect_height(&outer) < srcheight) + { + source.top += (srcheight - rect_height(&outer)) / 2; + source.bottom = source.top + rect_height(&outer); + } + + // match the destination and source sizes + dstwidth = srcwidth = source.right - source.left; + dstheight = srcheight = source.bottom - source.top; + } + else if (video_config.keepaspect) + { + // compute the appropriate visible area + render_target_compute_visible_area(window->target, rect_width(&outer), rect_height(&outer), winvideo_monitor_get_aspect(monitor), render_target_get_orientation(window->target), &dstwidth, &dstheight); + } + + // center within + dest.left = outer.left + (rect_width(&outer) - dstwidth) / 2; + dest.right = dest.left + dstwidth; + dest.top = outer.top + (rect_height(&outer) - dstheight) / 2; + dest.bottom = dest.top + dstheight; + + // compare against last destination; if different, force a redraw + if (dest.left != dd->lastdest.left || dest.right != dd->lastdest.right || dest.top != dd->lastdest.top || dest.bottom != dd->lastdest.bottom) + { + dd->lastdest = dest; + update_outer_rects(dd); + } + + // clear outer rects if we need to + if (dd->clearouter != 0) + { + dd->clearouter--; + + // clear the left edge + if (dest.left > outer.left) + { + clear = outer; + clear.right = dest.left; + result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result); + } + + // clear the right edge + if (dest.right < outer.right) + { + clear = outer; + clear.left = dest.right; + result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result); + } + + // clear the top edge + if (dest.top > outer.top) + { + clear = outer; + clear.bottom = dest.top; + result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result); + } + + // clear the bottom edge + if (dest.bottom < outer.bottom) + { + clear = outer; + clear.top = dest.bottom; + result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result); + } + } + + // do the blit + result = IDirectDrawSurface7_Blt(target, &dest, dd->blit, &source, DDBLT_WAIT, NULL); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X blitting to the screen\n", (int)result); + + // page flip if triple buffered + if (window->fullscreen && dd->back != NULL) + { + result = IDirectDrawSurface7_Flip(dd->primary, NULL, DDFLIP_WAIT); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X waiting for VBLANK\n", (int)result); + } +} + + + +//============================================================ +// config_adapter_mode +//============================================================ + +static int config_adapter_mode(win_window_info *window) +{ + DDDEVICEIDENTIFIER2 identifier; + dd_info *dd = window->drawdata; + HRESULT result; + + // choose the monitor number + get_adapter_for_monitor(dd, window->monitor); + + // create a temporary DirectDraw object + result = (*directdrawcreateex)(dd->adapter_ptr, (LPVOID *)&dd->ddraw, &IID_IDirectDraw7, NULL); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X during DirectDrawCreateEx call\n", (int)result); + return 1; + } + + // get the identifier + result = IDirectDraw7_GetDeviceIdentifier(dd->ddraw, &identifier, 0); + if (result != DD_OK) + { + mame_printf_error("Error getting identifier for device\n"); + return 1; + } + mame_printf_verbose("DirectDraw: Configuring device %s\n", identifier.szDescription); + + // get the current display mode + memset(&dd->origmode, 0, sizeof(dd->origmode)); + dd->origmode.dwSize = sizeof(dd->origmode); + result = IDirectDraw7_GetDisplayMode(dd->ddraw, &dd->origmode); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X getting current display mode\n", (int)result); + IDirectDraw7_Release(dd->ddraw); + return 1; + } + + // choose a resolution: full screen mode case + if (window->fullscreen) + { + // default to the current mode exactly + dd->width = dd->origmode.dwWidth; + dd->height = dd->origmode.dwHeight; + dd->refresh = dd->origmode.dwRefreshRate; + + // if we're allowed to switch resolutions, override with something better + if (video_config.switchres) + pick_best_mode(window); + } + + // release the DirectDraw object + IDirectDraw7_Release(dd->ddraw); + dd->ddraw = NULL; + + // if we're not changing resolutions, make sure we have a resolution we can handle + if (!window->fullscreen || !video_config.switchres) + { + switch (dd->origmode.ddpfPixelFormat.dwRBitMask) + { + case 0x00ff0000: + case 0x000000ff: + case 0xf800: + case 0x7c00: + break; + + default: + mame_printf_verbose("DirectDraw: Unknown target mode: R=%08X G=%08X B=%08X\n", (int)dd->origmode.ddpfPixelFormat.dwRBitMask, (int)dd->origmode.ddpfPixelFormat.dwGBitMask, (int)dd->origmode.ddpfPixelFormat.dwBBitMask); + return 1; + } + } + + return 0; +} + + + +//============================================================ +// monitor_enum_callback +//============================================================ + +static BOOL WINAPI monitor_enum_callback(GUID FAR *guid, LPSTR description, LPSTR name, LPVOID context, HMONITOR hmonitor) +{ + monitor_enum_info *einfo = (monitor_enum_info *)context; + + // do we match the desired monitor? + if (hmonitor == einfo->monitor->handle || (hmonitor == NULL && (einfo->monitor->info.dwFlags & MONITORINFOF_PRIMARY) != 0)) + { + einfo->guid_ptr = (guid != NULL) ? &einfo->guid : NULL; + if (guid != NULL) + einfo->guid = *guid; + einfo->foundit = TRUE; + } + return 1; +} + + + +//============================================================ +// get_adapter_for_monitor +//============================================================ + +static void get_adapter_for_monitor(dd_info *dd, win_monitor_info *monitor) +{ + monitor_enum_info einfo; + HRESULT result; + + // try to find our monitor + memset(&einfo, 0, sizeof(einfo)); + einfo.monitor = monitor; + result = (*directdrawenumerateex)(monitor_enum_callback, &einfo, DDENUM_ATTACHEDSECONDARYDEVICES); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X during DirectDrawEnumerateEx call\n", (int)result); + + // set up the adapter + if (einfo.foundit && einfo.guid_ptr != NULL) + { + dd->adapter = einfo.guid; + dd->adapter_ptr = &dd->adapter; + } + else + dd->adapter_ptr = NULL; +} + + + +//============================================================ +// pick_best_mode +//============================================================ + +static HRESULT WINAPI enum_modes_callback(LPDDSURFACEDESC2 desc, LPVOID context) +{ + float size_score, refresh_score, final_score; + mode_enum_info *einfo = context; + dd_info *dd = einfo->window->drawdata; + + // skip non-32 bit modes + if (desc->ddpfPixelFormat.dwRGBBitCount != 32) + return DDENUMRET_OK; + + // compute initial score based on difference between target and current + size_score = 1.0f / (1.0f + fabs((INT32)desc->dwWidth - einfo->target_width) + fabs((INT32)desc->dwHeight - einfo->target_height)); + + // if the mode is too small, give a big penalty + if (desc->dwWidth < einfo->minimum_width || desc->dwHeight < einfo->minimum_height) + size_score *= 0.01f; + + // if mode is smaller than we'd like, it only scores up to 0.1 + if (desc->dwWidth < einfo->target_width || desc->dwHeight < einfo->target_height) + size_score *= 0.1f; + + // if we're looking for a particular mode, that's a winner + if (desc->dwWidth == einfo->window->maxwidth && desc->dwHeight == einfo->window->maxheight) + size_score = 2.0f; + + // compute refresh score + refresh_score = 1.0f / (1.0f + fabs((double)desc->dwRefreshRate - einfo->target_refresh)); + + // if refresh is smaller than we'd like, it only scores up to 0.1 + if ((double)desc->dwRefreshRate < einfo->target_refresh) + refresh_score *= 0.1; + + // if we're looking for a particular refresh, make sure it matches + if (desc->dwRefreshRate == einfo->window->refresh) + refresh_score = 2.0f; + + // weight size and refresh equally + final_score = size_score + refresh_score; + + // best so far? + mame_printf_verbose(" %4dx%4d@%3dHz -> %f\n", (int)desc->dwWidth, (int)desc->dwHeight, (int)desc->dwRefreshRate, final_score * 1000.0f); + if (final_score > einfo->best_score) + { + einfo->best_score = final_score; + dd->width = desc->dwWidth; + dd->height = desc->dwHeight; + dd->refresh = desc->dwRefreshRate; + } + return DDENUMRET_OK; +} + + + +//============================================================ +// pick_best_mode +//============================================================ + +static void pick_best_mode(win_window_info *window) +{ + dd_info *dd = window->drawdata; + mode_enum_info einfo; + HRESULT result; + + // determine the minimum width/height for the selected target + // note: technically we should not be calling this from an alternate window + // thread; however, it is only done during init time, and the init code on + // the main thread is waiting for us to finish, so it is safe to do so here + render_target_get_minimum_size(window->target, &einfo.minimum_width, &einfo.minimum_height); + + // use those as the target for now + einfo.target_width = einfo.minimum_width * MAX(1, video_config.prescale); + einfo.target_height = einfo.minimum_height * MAX(1, video_config.prescale); + einfo.target_refresh = ATTOSECONDS_TO_HZ(Machine->screen[0].refresh); + + // if we're not stretching, allow some slop on the minimum since we can handle it + if (!video_config.hwstretch) + { + einfo.minimum_width -= 4; + einfo.minimum_height -= 4; + } + + // if we are stretching, aim for a mode approximately 2x the game's resolution + else if (video_config.prescale <= 1) + { + einfo.target_width *= 2; + einfo.target_height *= 2; + } + + // fill in the rest of the data + einfo.window = window; + einfo.best_score = 0.0f; + + // enumerate the modes + mame_printf_verbose("DirectDraw: Selecting video mode...\n"); + result = IDirectDraw7_EnumDisplayModes(dd->ddraw, DDEDM_REFRESHRATES, NULL, &einfo, enum_modes_callback); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X during EnumDisplayModes call\n", (int)result); + mame_printf_verbose("DirectDraw: Mode selected = %4dx%4d@%3dHz\n", dd->width, dd->height, dd->refresh); +} + + + +//============================================================ +// SOFTWARE RENDERING +//============================================================ + +#define FUNC_PREFIX(x) drawdd_rgb888_##x +#define PIXEL_TYPE UINT32 +#define SRCSHIFT_R 0 +#define SRCSHIFT_G 0 +#define SRCSHIFT_B 0 +#define DSTSHIFT_R 16 +#define DSTSHIFT_G 8 +#define DSTSHIFT_B 0 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_bgr888_##x +#define PIXEL_TYPE UINT32 +#define SRCSHIFT_R 0 +#define SRCSHIFT_G 0 +#define SRCSHIFT_B 0 +#define DSTSHIFT_R 0 +#define DSTSHIFT_G 8 +#define DSTSHIFT_B 16 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_rgb565_##x +#define PIXEL_TYPE UINT16 +#define SRCSHIFT_R 3 +#define SRCSHIFT_G 2 +#define SRCSHIFT_B 3 +#define DSTSHIFT_R 11 +#define DSTSHIFT_G 5 +#define DSTSHIFT_B 0 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_rgb555_##x +#define PIXEL_TYPE UINT16 +#define SRCSHIFT_R 3 +#define SRCSHIFT_G 3 +#define SRCSHIFT_B 3 +#define DSTSHIFT_R 10 +#define DSTSHIFT_G 5 +#define DSTSHIFT_B 0 + +#include "rendersw.c" + + + +//============================================================ +// SOFTWARE RENDERING -- NO READING VARIANTS +//============================================================ + +#define FUNC_PREFIX(x) drawdd_rgb888_nr_##x +#define PIXEL_TYPE UINT32 +#define SRCSHIFT_R 0 +#define SRCSHIFT_G 0 +#define SRCSHIFT_B 0 +#define DSTSHIFT_R 16 +#define DSTSHIFT_G 8 +#define DSTSHIFT_B 0 +#define NO_DEST_READ 1 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_bgr888_nr_##x +#define PIXEL_TYPE UINT32 +#define SRCSHIFT_R 0 +#define SRCSHIFT_G 0 +#define SRCSHIFT_B 0 +#define DSTSHIFT_R 0 +#define DSTSHIFT_G 8 +#define DSTSHIFT_B 16 +#define NO_DEST_READ 1 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_rgb565_nr_##x +#define PIXEL_TYPE UINT16 +#define SRCSHIFT_R 3 +#define SRCSHIFT_G 2 +#define SRCSHIFT_B 3 +#define DSTSHIFT_R 11 +#define DSTSHIFT_G 5 +#define DSTSHIFT_B 0 +#define NO_DEST_READ 1 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_rgb555_nr_##x +#define PIXEL_TYPE UINT16 +#define SRCSHIFT_R 3 +#define SRCSHIFT_G 3 +#define SRCSHIFT_B 3 +#define DSTSHIFT_R 10 +#define DSTSHIFT_G 5 +#define DSTSHIFT_B 0 +#define NO_DEST_READ 1 + +#include "rendersw.c" diff --git a/src/osd/windows/drawgdi.c b/src/osd/windows/drawgdi.c new file mode 100644 index 00000000000..05f1138c11f --- /dev/null +++ b/src/osd/windows/drawgdi.c @@ -0,0 +1,215 @@ +//============================================================ +// +// drawgdi.c - Win32 GDI drawing +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include + +// MAME headers +#include "mamecore.h" + +// MAMEOS headers +#include "window.h" + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +/* gdi_info is the information for the current screen */ +typedef struct _gdi_info gdi_info; +struct _gdi_info +{ + BITMAPINFO bminfo; + RGBQUAD colors[256]; + UINT8 * bmdata; + size_t bmsize; +}; + + + +//============================================================ +// PROTOTYPES +//============================================================ + +// core functions +static void drawgdi_exit(void); +static int drawgdi_window_init(win_window_info *window); +static void drawgdi_window_destroy(win_window_info *window); +static const render_primitive_list *drawgdi_window_get_primitives(win_window_info *window); +static int drawgdi_window_draw(win_window_info *window, HDC dc, int update); + +// rendering +static void drawgdi_rgb888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); + + + +//============================================================ +// drawgdi_init +//============================================================ + +int drawgdi_init(win_draw_callbacks *callbacks) +{ + // fill in the callbacks + callbacks->exit = drawgdi_exit; + callbacks->window_init = drawgdi_window_init; + callbacks->window_get_primitives = drawgdi_window_get_primitives; + callbacks->window_draw = drawgdi_window_draw; + callbacks->window_destroy = drawgdi_window_destroy; + return 0; +} + + + +//============================================================ +// drawgdi_exit +//============================================================ + +static void drawgdi_exit(void) +{ +} + + + +//============================================================ +// drawgdi_window_init +//============================================================ + +static int drawgdi_window_init(win_window_info *window) +{ + gdi_info *gdi; + int i; + + // allocate memory for our structures + gdi = malloc_or_die(sizeof(*gdi)); + memset(gdi, 0, sizeof(*gdi)); + window->drawdata = gdi; + + // fill in the bitmap info header + gdi->bminfo.bmiHeader.biSize = sizeof(gdi->bminfo.bmiHeader); + gdi->bminfo.bmiHeader.biPlanes = 1; + gdi->bminfo.bmiHeader.biCompression = BI_RGB; + gdi->bminfo.bmiHeader.biSizeImage = 0; + gdi->bminfo.bmiHeader.biXPelsPerMeter = 0; + gdi->bminfo.bmiHeader.biYPelsPerMeter = 0; + gdi->bminfo.bmiHeader.biClrUsed = 0; + gdi->bminfo.bmiHeader.biClrImportant = 0; + + // initialize the palette to a gray ramp + for (i = 0; i < 256; i++) + { + gdi->bminfo.bmiColors[i].rgbRed = i; + gdi->bminfo.bmiColors[i].rgbGreen = i; + gdi->bminfo.bmiColors[i].rgbBlue = i; + gdi->bminfo.bmiColors[i].rgbReserved = i; + } + + return 0; +} + + + +//============================================================ +// drawgdi_window_destroy +//============================================================ + +static void drawgdi_window_destroy(win_window_info *window) +{ + gdi_info *gdi = window->drawdata; + + // skip if nothing + if (gdi == NULL) + return; + + // free the bitmap memory + if (gdi->bmdata != NULL) + free(gdi->bmdata); + free(gdi); + window->drawdata = NULL; +} + + + +//============================================================ +// drawgdi_window_get_primitives +//============================================================ + +static const render_primitive_list *drawgdi_window_get_primitives(win_window_info *window) +{ + RECT client; + GetClientRect(window->hwnd, &client); + render_target_set_bounds(window->target, rect_width(&client), rect_height(&client), winvideo_monitor_get_aspect(window->monitor)); + return render_target_get_primitives(window->target); +} + + + +//============================================================ +// drawgdi_window_draw +//============================================================ + +static int drawgdi_window_draw(win_window_info *window, HDC dc, int update) +{ + gdi_info *gdi = window->drawdata; + int width, height, pitch; + RECT bounds; + + // we don't have any special resize behaviors + if (window->resize_state == RESIZE_STATE_PENDING) + window->resize_state = RESIZE_STATE_NORMAL; + + // get the target bounds + GetClientRect(window->hwnd, &bounds); + + // compute width/height/pitch of target + width = rect_width(&bounds); + height = rect_height(&bounds); + pitch = (width + 3) & ~3; + + // make sure our temporary bitmap is big enough + if (pitch * height * 4 > gdi->bmsize) + { + gdi->bmsize = pitch * height * 4 * 2; + gdi->bmdata = realloc(gdi->bmdata, gdi->bmsize); + } + + // draw the primitives to the bitmap + osd_lock_acquire(window->primlist->lock); + drawgdi_rgb888_draw_primitives(window->primlist->head, gdi->bmdata, width, height, pitch); + osd_lock_release(window->primlist->lock); + + // fill in bitmap-specific info + gdi->bminfo.bmiHeader.biWidth = pitch; + gdi->bminfo.bmiHeader.biHeight = -height; + gdi->bminfo.bmiHeader.biBitCount = 32; + + // blit to the screen + StretchDIBits(dc, 0, 0, width, height, + 0, 0, width, height, + gdi->bmdata, &gdi->bminfo, DIB_RGB_COLORS, SRCCOPY); + return 0; +} + + + +//============================================================ +// SOFTWARE RENDERING +//============================================================ + +#define FUNC_PREFIX(x) drawgdi_rgb888_##x +#define PIXEL_TYPE UINT32 +#define SRCSHIFT_R 0 +#define SRCSHIFT_G 0 +#define SRCSHIFT_B 0 +#define DSTSHIFT_R 16 +#define DSTSHIFT_G 8 +#define DSTSHIFT_B 0 + +#include "rendersw.c" diff --git a/src/osd/windows/drawnone.c b/src/osd/windows/drawnone.c new file mode 100644 index 00000000000..b2f84d8d50e --- /dev/null +++ b/src/osd/windows/drawnone.c @@ -0,0 +1,104 @@ +//============================================================ +// +// drawnone.c - stub "nothing" drawer +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include + +// MAME headers +#include "mamecore.h" + +// MAMEOS headers +#include "window.h" + + + +//============================================================ +// PROTOTYPES +//============================================================ + +// core functions +static void drawnone_exit(void); +static int drawnone_window_init(win_window_info *window); +static void drawnone_window_destroy(win_window_info *window); +static const render_primitive_list *drawnone_window_get_primitives(win_window_info *window); +static int drawnone_window_draw(win_window_info *window, HDC dc, int update); + + + +//============================================================ +// drawnone_init +//============================================================ + +int drawnone_init(win_draw_callbacks *callbacks) +{ + // fill in the callbacks + callbacks->exit = drawnone_exit; + callbacks->window_init = drawnone_window_init; + callbacks->window_get_primitives = drawnone_window_get_primitives; + callbacks->window_draw = drawnone_window_draw; + callbacks->window_destroy = drawnone_window_destroy; + return 0; +} + + + +//============================================================ +// drawnone_exit +//============================================================ + +static void drawnone_exit(void) +{ +} + + + +//============================================================ +// drawnone_window_init +//============================================================ + +static int drawnone_window_init(win_window_info *window) +{ + return 0; +} + + + +//============================================================ +// drawnone_window_destroy +//============================================================ + +static void drawnone_window_destroy(win_window_info *window) +{ +} + + + +//============================================================ +// drawnone_window_get_primitives +//============================================================ + +static const render_primitive_list *drawnone_window_get_primitives(win_window_info *window) +{ + RECT client; + GetClientRect(window->hwnd, &client); + render_target_set_bounds(window->target, rect_width(&client), rect_height(&client), winvideo_monitor_get_aspect(window->monitor)); + return render_target_get_primitives(window->target); +} + + + +//============================================================ +// drawnone_window_draw +//============================================================ + +static int drawnone_window_draw(win_window_info *window, HDC dc, int update) +{ + return 0; +} diff --git a/src/osd/windows/input.c b/src/osd/windows/input.c new file mode 100644 index 00000000000..42a23c713bf --- /dev/null +++ b/src/osd/windows/input.c @@ -0,0 +1,2129 @@ +//============================================================ +// +// input.c - Win32 implementation of MAME input routines +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// For testing purposes: force DirectInput +#define FORCE_DIRECTINPUT 0 + +// Needed for RAW Input +#define _WIN32_WINNT 0x501 + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +// undef WINNT for dinput.h to prevent duplicate definition +#undef WINNT +#ifdef DIRECTINPUT_VERSION +#undef DIRECTINPUT_VERSION +#endif +#define DIRECTINPUT_VERSION 0x0700 +#include + +// standard C headers +#include +#include +#include + +// MAME headers +#include "osdepend.h" +#include "driver.h" +#include "ui.h" + +// MAMEOS headers +#include "winmain.h" +#include "window.h" +#include "input.h" +#include "debugwin.h" +#include "video.h" +#include "strconv.h" +#include "config.h" + +#ifdef MESS +#include "uimess.h" +#endif + + + +//============================================================ +// PARAMETERS +//============================================================ + +enum +{ + POVDIR_LEFT = 0, + POVDIR_RIGHT, + POVDIR_UP, + POVDIR_DOWN +}; + +#define MAX_KEYS 256 + +#define MAME_KEY 0 +#define DI_KEY 1 +#define VIRTUAL_KEY 2 +#define ASCII_KEY 3 + + + +//============================================================ +// MACROS +//============================================================ + +#define STRUCTSIZE(x) ((dinput_version == 0x0300) ? sizeof(x##_DX3) : sizeof(x)) + +#ifdef UNICODE +#define UNICODE_SUFFIX "W" +#else +#define UNICODE_SUFFIX "A" +#endif + + + +//============================================================ +// TYPEDEFS +//============================================================ + +// state information for a keyboard; DirectInput state must be first element +typedef struct _keyboard_state keyboard_state; +struct _keyboard_state +{ + UINT8 state[MAX_KEYS]; + INT8 oldkey[MAX_KEYS]; + INT8 currkey[MAX_KEYS]; +}; + + +// state information for a mouse; DirectInput state must be first element +typedef struct _mouse_state mouse_state; +struct _mouse_state +{ + DIMOUSESTATE2 state; + mouse_state * partner; + LONG raw_x, raw_y, raw_z; +}; + + +// state information for a joystick; DirectInput state must be first element +typedef struct _joystick_state joystick_state; +struct _joystick_state +{ + DIJOYSTATE state; + LONG rangemin[8]; + LONG rangemax[8]; +}; + + +// DirectInput-specific information about a device +typedef struct _dinput_device_info dinput_device_info; +struct _dinput_device_info +{ + LPDIRECTINPUTDEVICE device; + LPDIRECTINPUTDEVICE2 device2; + DIDEVCAPS caps; + LPCDIDATAFORMAT format; +}; + + +// RawInput-specific information about a device +typedef struct _rawinput_device_info rawinput_device_info; +struct _rawinput_device_info +{ + HANDLE device; +}; + + +// generic device information +typedef struct _device_info device_info; +struct _device_info +{ + // device information + device_info ** head; + device_info * next; + const char * name; + void (*poll)(device_info *info); + + // MAME information + input_device * device; + + // device state + union + { + keyboard_state keyboard; + mouse_state mouse; + joystick_state joystick; + }; + + // DirectInput/RawInput-specific state + dinput_device_info dinput; + rawinput_device_info rawinput; +}; + + +// RawInput APIs +typedef WINUSERAPI INT (WINAPI *get_rawinput_device_list_ptr)(OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PINT puiNumDevices, IN UINT cbSize); +typedef WINUSERAPI INT (WINAPI *get_rawinput_data_ptr)(IN HRAWINPUT hRawInput, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize, IN UINT cbSizeHeader); +typedef WINUSERAPI INT (WINAPI *get_rawinput_device_info_ptr)(IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize); +typedef WINUSERAPI BOOL (WINAPI *register_rawinput_devices_ptr)(IN PCRAWINPUTDEVICE pRawInputDevices, IN UINT uiNumDevices, IN UINT cbSize); + + + +//============================================================ +// LOCAL VARIABLES +//============================================================ + +// global states +static UINT8 input_enabled; +static osd_lock * input_lock; +static UINT8 input_paused; +static DWORD last_poll; + +// DirectInput variables +static LPDIRECTINPUT dinput; +static int dinput_version; + +// RawInput variables +static get_rawinput_device_list_ptr get_rawinput_device_list; +static get_rawinput_data_ptr get_rawinput_data; +static get_rawinput_device_info_ptr get_rawinput_device_info; +static register_rawinput_devices_ptr register_rawinput_devices; + +// keyboard states +static UINT8 keyboard_win32_reported_key_down; +static device_info * keyboard_list; + +// mouse states +static UINT8 mouse_enabled; +static device_info * mouse_list; + +// lightgun states +static UINT8 lightgun_shared_axis_mode; +static UINT8 lightgun_enabled; +static device_info * lightgun_list; + +// joystick states +static device_info * joystick_list; + +// default axis names +static const TCHAR *default_axis_name[] = +{ + TEXT("X"), TEXT("Y"), TEXT("Z"), TEXT("RX"), + TEXT("RY"), TEXT("RZ"), TEXT("SL1"), TEXT("SL2") +}; + + + +//============================================================ +// PROTOTYPES +//============================================================ + +static void wininput_pause(running_machine *machine, int paused); +static void wininput_exit(running_machine *machine); + +// device list management +static void device_list_poll_devices(device_info *devlist_head); +static void device_list_reset_devices(device_info *devlist_head); + +// generic device management +static device_info *generic_device_alloc(device_info **devlist_head_ptr, const TCHAR *name); +static void generic_device_free(device_info *devinfo); +static int generic_device_index(device_info *devlist_head, device_info *devinfo); +static void generic_device_reset(device_info *devinfo); +static INT32 generic_button_get_state(void *device_internal, void *item_internal); +static INT32 generic_axis_get_state(void *device_internal, void *item_internal); + +// Win32-specific input code +static void win32_init(running_machine *machine); +static void win32_exit(running_machine *machine); +static void win32_keyboard_poll(device_info *devinfo); +static void win32_lightgun_poll(device_info *devinfo); + +// DirectInput-specific code +static void dinput_init(running_machine *machine); +static void dinput_exit(running_machine *machine); +static HRESULT dinput_set_dword_property(LPDIRECTINPUTDEVICE device, REFGUID property_guid, DWORD object, DWORD how, DWORD value); +static device_info *dinput_device_create(device_info **devlist_head_ptr, LPCDIDEVICEINSTANCE instance, LPCDIDATAFORMAT format1, LPCDIDATAFORMAT format2, DWORD cooperative_level); +static void dinput_device_release(device_info *devinfo); +static const char *dinput_device_item_name(device_info *devinfo, int offset, const TCHAR *defstring, const TCHAR *suffix); +static HRESULT dinput_device_poll(device_info *devinfo); +static BOOL CALLBACK dinput_keyboard_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref); +static void dinput_keyboard_poll(device_info *devinfo); +static BOOL CALLBACK dinput_mouse_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref); +static void dinput_mouse_poll(device_info *devinfo); +static BOOL CALLBACK dinput_joystick_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref); +static void dinput_joystick_poll(device_info *devinfo); +static INT32 dinput_joystick_pov_get_state(void *device_internal, void *item_internal); + +// RawInput-specific code +static void rawinput_init(running_machine *machine); +static void rawinput_exit(running_machine *machine); +static device_info *rawinput_device_create(device_info **devlist_head_ptr, PRAWINPUTDEVICELIST device); +static void rawinput_device_release(device_info *info); +static TCHAR *rawinput_device_improve_name(TCHAR *name); +static void rawinput_keyboard_enum(PRAWINPUTDEVICELIST device); +static void rawinput_keyboard_update(HANDLE device, RAWKEYBOARD *data); +static void rawinput_mouse_enum(PRAWINPUTDEVICELIST device); +static void rawinput_mouse_update(HANDLE device, RAWMOUSE *data); +static void rawinput_mouse_poll(device_info *devinfo); + +// misc utilities +static TCHAR *reg_query_string(HKEY key, const TCHAR *path); +static const TCHAR *default_button_name(int which); +static const TCHAR *default_pov_name(int which); + + + +//============================================================ +// KEYBOARD/JOYSTICK LIST +//============================================================ + +// master keyboard translation table +static const int win_key_trans_table[][4] = +{ + // MAME key dinput key virtual key ascii + { ITEM_ID_ESC, DIK_ESCAPE, VK_ESCAPE, 27 }, + { ITEM_ID_1, DIK_1, '1', '1' }, + { ITEM_ID_2, DIK_2, '2', '2' }, + { ITEM_ID_3, DIK_3, '3', '3' }, + { ITEM_ID_4, DIK_4, '4', '4' }, + { ITEM_ID_5, DIK_5, '5', '5' }, + { ITEM_ID_6, DIK_6, '6', '6' }, + { ITEM_ID_7, DIK_7, '7', '7' }, + { ITEM_ID_8, DIK_8, '8', '8' }, + { ITEM_ID_9, DIK_9, '9', '9' }, + { ITEM_ID_0, DIK_0, '0', '0' }, + { ITEM_ID_MINUS, DIK_MINUS, VK_OEM_MINUS, '-' }, + { ITEM_ID_EQUALS, DIK_EQUALS, VK_OEM_PLUS, '=' }, + { ITEM_ID_BACKSPACE, DIK_BACK, VK_BACK, 8 }, + { ITEM_ID_TAB, DIK_TAB, VK_TAB, 9 }, + { ITEM_ID_Q, DIK_Q, 'Q', 'Q' }, + { ITEM_ID_W, DIK_W, 'W', 'W' }, + { ITEM_ID_E, DIK_E, 'E', 'E' }, + { ITEM_ID_R, DIK_R, 'R', 'R' }, + { ITEM_ID_T, DIK_T, 'T', 'T' }, + { ITEM_ID_Y, DIK_Y, 'Y', 'Y' }, + { ITEM_ID_U, DIK_U, 'U', 'U' }, + { ITEM_ID_I, DIK_I, 'I', 'I' }, + { ITEM_ID_O, DIK_O, 'O', 'O' }, + { ITEM_ID_P, DIK_P, 'P', 'P' }, + { ITEM_ID_OPENBRACE, DIK_LBRACKET, VK_OEM_4, '[' }, + { ITEM_ID_CLOSEBRACE, DIK_RBRACKET, VK_OEM_6, ']' }, + { ITEM_ID_ENTER, DIK_RETURN, VK_RETURN, 13 }, + { ITEM_ID_LCONTROL, DIK_LCONTROL, VK_LCONTROL, 0 }, + { ITEM_ID_A, DIK_A, 'A', 'A' }, + { ITEM_ID_S, DIK_S, 'S', 'S' }, + { ITEM_ID_D, DIK_D, 'D', 'D' }, + { ITEM_ID_F, DIK_F, 'F', 'F' }, + { ITEM_ID_G, DIK_G, 'G', 'G' }, + { ITEM_ID_H, DIK_H, 'H', 'H' }, + { ITEM_ID_J, DIK_J, 'J', 'J' }, + { ITEM_ID_K, DIK_K, 'K', 'K' }, + { ITEM_ID_L, DIK_L, 'L', 'L' }, + { ITEM_ID_COLON, DIK_SEMICOLON, VK_OEM_1, ';' }, + { ITEM_ID_QUOTE, DIK_APOSTROPHE, VK_OEM_7, '\'' }, + { ITEM_ID_TILDE, DIK_GRAVE, VK_OEM_3, '`' }, + { ITEM_ID_LSHIFT, DIK_LSHIFT, VK_LSHIFT, 0 }, + { ITEM_ID_BACKSLASH, DIK_BACKSLASH, VK_OEM_5, '\\' }, + { ITEM_ID_Z, DIK_Z, 'Z', 'Z' }, + { ITEM_ID_X, DIK_X, 'X', 'X' }, + { ITEM_ID_C, DIK_C, 'C', 'C' }, + { ITEM_ID_V, DIK_V, 'V', 'V' }, + { ITEM_ID_B, DIK_B, 'B', 'B' }, + { ITEM_ID_N, DIK_N, 'N', 'N' }, + { ITEM_ID_M, DIK_M, 'M', 'M' }, + { ITEM_ID_COMMA, DIK_COMMA, VK_OEM_COMMA, ',' }, + { ITEM_ID_STOP, DIK_PERIOD, VK_OEM_PERIOD, '.' }, + { ITEM_ID_SLASH, DIK_SLASH, VK_OEM_2, '/' }, + { ITEM_ID_RSHIFT, DIK_RSHIFT, VK_RSHIFT, 0 }, + { ITEM_ID_ASTERISK, DIK_MULTIPLY, VK_MULTIPLY, '*' }, + { ITEM_ID_LALT, DIK_LMENU, VK_LMENU, 0 }, + { ITEM_ID_SPACE, DIK_SPACE, VK_SPACE, ' ' }, + { ITEM_ID_CAPSLOCK, DIK_CAPITAL, VK_CAPITAL, 0 }, + { ITEM_ID_F1, DIK_F1, VK_F1, 0 }, + { ITEM_ID_F2, DIK_F2, VK_F2, 0 }, + { ITEM_ID_F3, DIK_F3, VK_F3, 0 }, + { ITEM_ID_F4, DIK_F4, VK_F4, 0 }, + { ITEM_ID_F5, DIK_F5, VK_F5, 0 }, + { ITEM_ID_F6, DIK_F6, VK_F6, 0 }, + { ITEM_ID_F7, DIK_F7, VK_F7, 0 }, + { ITEM_ID_F8, DIK_F8, VK_F8, 0 }, + { ITEM_ID_F9, DIK_F9, VK_F9, 0 }, + { ITEM_ID_F10, DIK_F10, VK_F10, 0 }, + { ITEM_ID_NUMLOCK, DIK_NUMLOCK, VK_NUMLOCK, 0 }, + { ITEM_ID_SCRLOCK, DIK_SCROLL, VK_SCROLL, 0 }, + { ITEM_ID_7_PAD, DIK_NUMPAD7, VK_NUMPAD7, 0 }, + { ITEM_ID_8_PAD, DIK_NUMPAD8, VK_NUMPAD8, 0 }, + { ITEM_ID_9_PAD, DIK_NUMPAD9, VK_NUMPAD9, 0 }, + { ITEM_ID_MINUS_PAD, DIK_SUBTRACT, VK_SUBTRACT, 0 }, + { ITEM_ID_4_PAD, DIK_NUMPAD4, VK_NUMPAD4, 0 }, + { ITEM_ID_5_PAD, DIK_NUMPAD5, VK_NUMPAD5, 0 }, + { ITEM_ID_6_PAD, DIK_NUMPAD6, VK_NUMPAD6, 0 }, + { ITEM_ID_PLUS_PAD, DIK_ADD, VK_ADD, 0 }, + { ITEM_ID_1_PAD, DIK_NUMPAD1, VK_NUMPAD1, 0 }, + { ITEM_ID_2_PAD, DIK_NUMPAD2, VK_NUMPAD2, 0 }, + { ITEM_ID_3_PAD, DIK_NUMPAD3, VK_NUMPAD3, 0 }, + { ITEM_ID_0_PAD, DIK_NUMPAD0, VK_NUMPAD0, 0 }, + { ITEM_ID_DEL_PAD, DIK_DECIMAL, VK_DECIMAL, 0 }, + { ITEM_ID_F11, DIK_F11, VK_F11, 0 }, + { ITEM_ID_F12, DIK_F12, VK_F12, 0 }, + { ITEM_ID_F13, DIK_F13, VK_F13, 0 }, + { ITEM_ID_F14, DIK_F14, VK_F14, 0 }, + { ITEM_ID_F15, DIK_F15, VK_F15, 0 }, + { ITEM_ID_ENTER_PAD, DIK_NUMPADENTER, VK_RETURN, 0 }, + { ITEM_ID_RCONTROL, DIK_RCONTROL, VK_RCONTROL, 0 }, + { ITEM_ID_SLASH_PAD, DIK_DIVIDE, VK_DIVIDE, 0 }, + { ITEM_ID_PRTSCR, DIK_SYSRQ, 0, 0 }, + { ITEM_ID_RALT, DIK_RMENU, VK_RMENU, 0 }, + { ITEM_ID_HOME, DIK_HOME, VK_HOME, 0 }, + { ITEM_ID_UP, DIK_UP, VK_UP, 0 }, + { ITEM_ID_PGUP, DIK_PRIOR, VK_PRIOR, 0 }, + { ITEM_ID_LEFT, DIK_LEFT, VK_LEFT, 0 }, + { ITEM_ID_RIGHT, DIK_RIGHT, VK_RIGHT, 0 }, + { ITEM_ID_END, DIK_END, VK_END, 0 }, + { ITEM_ID_DOWN, DIK_DOWN, VK_DOWN, 0 }, + { ITEM_ID_PGDN, DIK_NEXT, VK_NEXT, 0 }, + { ITEM_ID_INSERT, DIK_INSERT, VK_INSERT, 0 }, + { ITEM_ID_DEL, DIK_DELETE, VK_DELETE, 0 }, + { ITEM_ID_LWIN, DIK_LWIN, VK_LWIN, 0 }, + { ITEM_ID_RWIN, DIK_RWIN, VK_RWIN, 0 }, + { ITEM_ID_MENU, DIK_APPS, VK_APPS, 0 }, + { ITEM_ID_PAUSE, DIK_PAUSE, VK_PAUSE, 0 }, + { ITEM_ID_CANCEL, 0, VK_CANCEL, 0 }, + + // New keys introduced in Windows 2000. These have no MAME codes to + // preserve compatibility with old config files that may refer to them + // as e.g. FORWARD instead of e.g. KEYCODE_WEBFORWARD. They need table + // entries anyway because otherwise they aren't recognized when + // GetAsyncKeyState polling is used (as happens currently when MAME is + // paused). Some codes are missing because the mapping to vkey codes + // isn't clear, and MapVirtualKey is no help. + + { ITEM_ID_OTHER_SWITCH, DIK_MUTE, VK_VOLUME_MUTE, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_VOLUMEDOWN, VK_VOLUME_DOWN, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_VOLUMEUP, VK_VOLUME_UP, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_WEBHOME, VK_BROWSER_HOME, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_WEBSEARCH, VK_BROWSER_SEARCH, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_WEBFAVORITES, VK_BROWSER_FAVORITES, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_WEBREFRESH, VK_BROWSER_REFRESH, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_WEBSTOP, VK_BROWSER_STOP, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_WEBFORWARD, VK_BROWSER_FORWARD, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_WEBBACK, VK_BROWSER_BACK, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_MAIL, VK_LAUNCH_MAIL, 0 }, + { ITEM_ID_OTHER_SWITCH, DIK_MEDIASELECT, VK_LAUNCH_MEDIA_SELECT, 0 }, +}; + + + +//============================================================ +// INLINE FUNCTIONS +//============================================================ + +INLINE void poll_if_necessary(void) +{ + // make sure we poll at least once every 1/4 second + if (GetTickCount() > last_poll + 1000 / 4) + wininput_poll(); +} + + +INLINE input_item_id keyboard_map_scancode_to_itemid(int scancode) +{ + int tablenum; + + // scan the table for a match + for (tablenum = 0; tablenum < ARRAY_LENGTH(win_key_trans_table); tablenum++) + if (win_key_trans_table[tablenum][DI_KEY] == scancode) + return win_key_trans_table[tablenum][MAME_KEY]; + + // default to an "other" switch + return ITEM_ID_OTHER_SWITCH; +} + + +INLINE INT32 normalize_absolute_axis(INT32 raw, INT32 rawmin, INT32 rawmax) +{ + INT32 center = (rawmax + rawmin) / 2; + + // make sure we have valid data + if (rawmin >= rawmax) + return raw; + + // above center + if (raw >= center) + { + INT32 result = (INT64)(raw - center) * (INT64)INPUT_ABSOLUTE_MAX / (INT64)(rawmax - center); + return MIN(result, INPUT_ABSOLUTE_MAX); + } + + // below center + else + { + INT32 result = -((INT64)(center - raw) * (INT64)-INPUT_ABSOLUTE_MIN / (INT64)(center - rawmin)); + return MAX(result, INPUT_ABSOLUTE_MIN); + } +} + + + +//============================================================ +// wininput_init +//============================================================ + +void wininput_init(running_machine *machine) +{ + // we need pause and exit callbacks + add_pause_callback(machine, wininput_pause); + add_exit_callback(machine, wininput_exit); + + // allocate a lock for input synchronizations, since messages sometimes come from another thread + input_lock = osd_lock_alloc(); + assert_always(input_lock != NULL, "Failed to allocate input_lock"); + + // decode the options + lightgun_shared_axis_mode = options_get_bool(mame_options(), WINOPTION_DUAL_LIGHTGUN); + + // initialize RawInput and DirectInput (RawInput first so we can fall back) + rawinput_init(machine); + dinput_init(machine); + win32_init(machine); + + // poll once to get the initial states + input_enabled = TRUE; + wininput_poll(); +} + + +//============================================================ +// wininput_pause +//============================================================ + +static void wininput_pause(running_machine *machine, int paused) +{ + // keep track of the paused state + input_paused = paused; +} + + +//============================================================ +// wininput_exit +//============================================================ + +static void wininput_exit(running_machine *machine) +{ + // acquire the lock and turn off input (this ensures everyone is done) + osd_lock_acquire(input_lock); + input_enabled = FALSE; + osd_lock_release(input_lock); + + // free the lock + osd_lock_free(input_lock); +} + + +//============================================================ +// wininput_poll +//============================================================ + +void wininput_poll(void) +{ + int hasfocus = winwindow_has_focus(); + + // ignore if not enabled + if (!input_enabled) + return; + + // remember when this happened + last_poll = GetTickCount(); + + // periodically process events, in case they're not coming through + // this also will make sure the mouse state is up-to-date + winwindow_process_events_periodic(); + + // track if mouse/lightgun is enabled, for mouse hiding purposes + mouse_enabled = input_device_class_enabled(DEVICE_CLASS_MOUSE); + lightgun_enabled = input_device_class_enabled(DEVICE_CLASS_LIGHTGUN); + + // poll all of the devices + if (hasfocus) + { + device_list_poll_devices(keyboard_list); + device_list_poll_devices(mouse_list); + device_list_poll_devices(lightgun_list); + device_list_poll_devices(joystick_list); + } + else + { + device_list_reset_devices(keyboard_list); + device_list_reset_devices(mouse_list); + device_list_reset_devices(lightgun_list); + device_list_reset_devices(joystick_list); + } +} + + +//============================================================ +// wininput_should_hide_mouse +//============================================================ + +int wininput_should_hide_mouse(void) +{ + // if we are paused or disabled, no + if (input_paused || !input_enabled) + return FALSE; + + // if neither mice nor lightguns enabled in the core, then no + if (!mouse_enabled && !lightgun_enabled) + return FALSE; + + // if the window has a menu, no + if (win_window_list != NULL && win_has_menu(win_window_list)) + return FALSE; + + // otherwise, yes + return TRUE; +} + + +//============================================================ +// wininput_handle_mouse_button +//============================================================ + +void wininput_handle_mouse_button(int button, int down, int x, int y) +{ + device_info *devinfo; + + // ignore if not enabled + if (!input_enabled) + return; + + // only need this for shared axis hack + if (!lightgun_shared_axis_mode || button >= 4) + return; + + // choose a device based on the button + devinfo = lightgun_list; + if (button >= 2 && devinfo != NULL) + { + button -= 2; + devinfo = devinfo->next; + } + + // take the lock + osd_lock_acquire(input_lock); + + // set the button state + devinfo->mouse.state.rgbButtons[button] = down ? 0x80 : 0x00; + if (down) + { + RECT client_rect; + POINT mousepos; + + // get the position relative to the window + GetClientRect(win_window_list->hwnd, &client_rect); + mousepos.x = x; + mousepos.y = y; + ScreenToClient(win_window_list->hwnd, &mousepos); + + // convert to absolute coordinates + devinfo->mouse.state.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right); + devinfo->mouse.state.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom); + } + + // release the lock + osd_lock_release(input_lock); +} + + +//============================================================ +// wininput_handle_raw +//============================================================ + +BOOL wininput_handle_raw(HANDLE device) +{ + BYTE small_buffer[4096]; + LPBYTE data = small_buffer; + BOOL result = FALSE; + int size; + + // ignore if not enabled + if (!input_enabled) + return result; + + // determine the size of databuffer we need + if ((*get_rawinput_data)((HRAWINPUT)device, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) != 0) + return result; + + // if necessary, allocate a temporary buffer and fetch the data + if (size > sizeof(small_buffer)) + { + data = malloc(size); + if (data == NULL) + return result; + } + + // fetch the data and process the appropriate message types + result = (*get_rawinput_data)((HRAWINPUT)device, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER)); + if (result != 0) + { + RAWINPUT *input = (RAWINPUT *)data; + + // handle keyboard input + if (input->header.dwType == RIM_TYPEKEYBOARD) + { + osd_lock_acquire(input_lock); + rawinput_keyboard_update(input->header.hDevice, &input->data.keyboard); + osd_lock_release(input_lock); + result = TRUE; + } + + // handle mouse input + else if (input->header.dwType == RIM_TYPEMOUSE) + { + osd_lock_acquire(input_lock); + rawinput_mouse_update(input->header.hDevice, &input->data.mouse); + osd_lock_release(input_lock); + result = TRUE; + } + } + + // free the temporary buffer and return the result + if (data != small_buffer) + free(data); + return result; +} + + +//============================================================ +// osd_customize_inputport_list +//============================================================ + +void osd_customize_inputport_list(input_port_default_entry *defaults) +{ + input_port_default_entry *idef = defaults; + + // loop over all the defaults + for (idef = defaults; idef->type != IPT_END; idef++) + switch (idef->type) + { + // disable the config menu if the ALT key is down + // (allows ALT-TAB to switch between windows apps) + case IPT_UI_CONFIGURE: + input_seq_set_5(&idef->defaultseq, KEYCODE_TAB, SEQCODE_NOT, KEYCODE_LALT, SEQCODE_NOT, KEYCODE_RALT); + break; + + // alt-enter for fullscreen + case IPT_OSD_1: + idef->token = "TOGGLE_FULLSCREEN"; + idef->name = "Toggle Fullscreen"; + input_seq_set_2(&idef->defaultseq, KEYCODE_LALT, KEYCODE_ENTER); + break; + +#ifdef MESS + case IPT_OSD_2: + if (mess_use_new_ui()) + { + idef->token = "TOGGLE_MENUBAR"; + idef->name = "Toggle Menubar"; + input_seq_set_1 (&idef->defaultseq, KEYCODE_SCRLOCK); + } + break; + + case IPT_UI_THROTTLE: + input_seq_set_0(&idef->defaultseq); + break; +#endif /* MESS */ + } +} + + +//============================================================ +// device_list_poll_devices +//============================================================ + +static void device_list_poll_devices(device_info *devlist_head) +{ + device_info *curdev; + + for (curdev = devlist_head; curdev != NULL; curdev = curdev->next) + if (curdev->poll != NULL) + (*curdev->poll)(curdev); +} + + +//============================================================ +// device_list_reset_devices +//============================================================ + +static void device_list_reset_devices(device_info *devlist_head) +{ + device_info *curdev; + + for (curdev = devlist_head; curdev != NULL; curdev = curdev->next) + generic_device_reset(curdev); +} + + +//============================================================ +// generic_device_alloc +//============================================================ + +static device_info *generic_device_alloc(device_info **devlist_head_ptr, const TCHAR *name) +{ + device_info **curdev_ptr; + device_info *devinfo; + + // allocate memory for the device object + devinfo = malloc_or_die(sizeof(*devinfo)); + memset(devinfo, 0, sizeof(*devinfo)); + devinfo->head = devlist_head_ptr; + + // allocate a UTF8 copy of the name + devinfo->name = utf8_from_tstring(name); + if (devinfo->name == NULL) + goto error; + + // append us to the list + for (curdev_ptr = devinfo->head; *curdev_ptr != NULL; curdev_ptr = &(*curdev_ptr)->next) ; + *curdev_ptr = devinfo; + + return devinfo; + +error: + free(devinfo); + return NULL; +} + + +//============================================================ +// generic_device_free +//============================================================ + +static void generic_device_free(device_info *devinfo) +{ + device_info **curdev_ptr; + + // remove us from the list + for (curdev_ptr = devinfo->head; *curdev_ptr != devinfo && *curdev_ptr != NULL; curdev_ptr = &(*curdev_ptr)->next) ; + if (*curdev_ptr == devinfo) + *curdev_ptr = devinfo->next; + + // free the copy of the name if present + if (devinfo->name != NULL) + free((void *)devinfo->name); + devinfo->name = NULL; + + // and now free the info + free(devinfo); +} + + +//============================================================ +// generic_device_index +//============================================================ + +static int generic_device_index(device_info *devlist_head, device_info *devinfo) +{ + int index = 0; + while (devlist_head != NULL) + { + if (devlist_head == devinfo) + return index; + index++; + devlist_head = devlist_head->next; + } + return -1; +} + + +//============================================================ +// generic_device_reset +//============================================================ + +static void generic_device_reset(device_info *devinfo) +{ + // keyboard case + if (devinfo->head == &keyboard_list) + memset(devinfo->keyboard.state, 0, sizeof(devinfo->keyboard.state)); + + // mouse/lightgun case + else if (devinfo->head == &mouse_list || devinfo->head == &lightgun_list) + memset(&devinfo->mouse.state, 0, sizeof(devinfo->mouse.state)); + + // joystick case + else if (devinfo->head == &joystick_list) + { + int povnum; + + memset(&devinfo->joystick.state, 0, sizeof(devinfo->joystick.state)); + for (povnum = 0; povnum < ARRAY_LENGTH(devinfo->joystick.state.rgdwPOV); povnum++) + devinfo->joystick.state.rgdwPOV[povnum] = 0xffff; + } +} + + +//============================================================ +// generic_button_get_state +//============================================================ + +static INT32 generic_button_get_state(void *device_internal, void *item_internal) +{ + BYTE *itemdata = item_internal; + + // return the current state + poll_if_necessary(); + return *itemdata >> 7; +} + + +//============================================================ +// generic_axis_get_state +//============================================================ + +static INT32 generic_axis_get_state(void *device_internal, void *item_internal) +{ + LONG *axisdata = item_internal; + + // return the current state + poll_if_necessary(); + return *axisdata; +} + + +//============================================================ +// win32_init +//============================================================ + +static void win32_init(running_machine *machine) +{ + int gunnum; + + // we don't need any initialization unless we are using shared axis mode for lightguns + if (!lightgun_shared_axis_mode) + return; + + // we need an exit callback + add_exit_callback(machine, win32_exit); + + // allocate two lightgun devices + for (gunnum = 0; gunnum < 2; gunnum++) + { + static const TCHAR *gun_names[] = { TEXT("Shared Axis Gun 1"), TEXT("Shared Axis Gun 2") }; + device_info *devinfo; + int axisnum, butnum; + + // allocate a device + devinfo = generic_device_alloc(&lightgun_list, gun_names[gunnum]); + if (devinfo == NULL) + break; + + // add the device + devinfo->device = input_device_add(DEVICE_CLASS_LIGHTGUN, devinfo->name, devinfo); + + // populate the axes + for (axisnum = 0; axisnum < 2; axisnum++) + { + const char *name = utf8_from_tstring(default_axis_name[axisnum]); + input_device_item_add(devinfo->device, name, &devinfo->mouse.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state); + free((void *)name); + } + + // populate the buttons + for (butnum = 0; butnum < 2; butnum++) + { + const char *name = utf8_from_tstring(default_button_name(butnum)); + input_device_item_add(devinfo->device, name, &devinfo->mouse.state.rgbButtons[butnum], ITEM_ID_BUTTON1 + butnum, generic_button_get_state); + free((void *)name); + } + } +} + + +//============================================================ +// win32_exit +//============================================================ + +static void win32_exit(running_machine *machine) +{ + // skip if we're in shared axis mode + if (!lightgun_shared_axis_mode) + return; + + // delete the lightgun devices + while (lightgun_list != NULL) + generic_device_free(lightgun_list); +} + + +//============================================================ +// win32_keyboard_poll +//============================================================ + +static void win32_keyboard_poll(device_info *devinfo) +{ + int keynum; + + // clear the flag that says we detected a key down via win32 + keyboard_win32_reported_key_down = FALSE; + + // reset the keyboard state and then repopulate + memset(devinfo->keyboard.state, 0, sizeof(devinfo->keyboard.state)); + + // iterate over keys + for (keynum = 0; keynum < ARRAY_LENGTH(win_key_trans_table); keynum++) + { + int vk = win_key_trans_table[keynum][VIRTUAL_KEY]; + if (vk != 0 && (GetAsyncKeyState(vk) & 0x8000) != 0) + { + int dik = win_key_trans_table[keynum][DI_KEY]; + + // conver the VK code to a scancode (DIK code) + if (dik != 0) + devinfo->keyboard.state[dik] = 0x80; + + // set this flag so that we continue to use win32 until all keys are up + keyboard_win32_reported_key_down = TRUE; + } + } +} + + +//============================================================ +// win32_lightgun_poll +//============================================================ + +static void win32_lightgun_poll(device_info *devinfo) +{ + INT32 xpos = 0, ypos = 0; + POINT mousepos; + + // if we are using the shared axis hack, the data is updated via Windows messages only + if (lightgun_shared_axis_mode) + return; + + // get the cursor position and transform into final results + GetCursorPos(&mousepos); + if (win_window_list != NULL) + { + RECT client_rect; + + // get the position relative to the window + GetClientRect(win_window_list->hwnd, &client_rect); + ScreenToClient(win_window_list->hwnd, &mousepos); + + // convert to absolute coordinates + xpos = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right); + ypos = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom); + } + + // update the X/Y positions + devinfo->mouse.state.lX = xpos; + devinfo->mouse.state.lY = ypos; +} + + +//============================================================ +// dinput_init +//============================================================ + +static void dinput_init(running_machine *machine) +{ + HRESULT result; + + // first attempt to initialize DirectInput at the current version + dinput_version = DIRECTINPUT_VERSION; + result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL); + if (result != DI_OK) + { + // if that fails, try version 5 + dinput_version = 0x0500; + result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL); + if (result != DI_OK) + { + // if that fails, try version 3 + dinput_version = 0x0300; + result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL); + if (result != DI_OK) + { + dinput_version = 0; + return; + } + } + } + mame_printf_verbose("DirectInput: Using DirectInput %d\n", dinput_version >> 8); + + // we need an exit callback + add_exit_callback(machine, dinput_exit); + + // initialize keyboard devices, but only if we don't have any yet + if (keyboard_list == NULL) + { + // enumerate the ones we have + result = IDirectInput_EnumDevices(dinput, DIDEVTYPE_KEYBOARD, dinput_keyboard_enum, 0, DIEDFL_ATTACHEDONLY); + if (result != DI_OK) + fatalerror("DirectInput: Unable to enumerate keyboards (result=%08X)\n", (UINT32)result); + } + + // initialize mouse & lightgun devices, but only if we don't have any yet + if (mouse_list == NULL) + { + // enumerate the ones we have + result = IDirectInput_EnumDevices(dinput, DIDEVTYPE_MOUSE, dinput_mouse_enum, 0, DIEDFL_ATTACHEDONLY); + if (result != DI_OK) + fatalerror("DirectInput: Unable to enumerate mice (result=%08X)\n", (UINT32)result); + } + + // initialize joystick devices + result = IDirectInput_EnumDevices(dinput, DIDEVTYPE_JOYSTICK, dinput_joystick_enum, 0, DIEDFL_ATTACHEDONLY); + if (result != DI_OK) + fatalerror("DirectInput: Unable to enumerate joysticks (result=%08X)\n", (UINT32)result); +} + + +//============================================================ +// dinput_exit +//============================================================ + +static void dinput_exit(running_machine *machine) +{ + // release all our devices + while (joystick_list != NULL && joystick_list->dinput.device != NULL) + dinput_device_release(joystick_list); + while (lightgun_list != NULL && lightgun_list->dinput.device != NULL) + dinput_device_release(lightgun_list); + while (mouse_list != NULL && mouse_list->dinput.device != NULL) + dinput_device_release(mouse_list); + while (keyboard_list != NULL && keyboard_list->dinput.device != NULL) + dinput_device_release(keyboard_list); + + // release DirectInput + if (dinput != NULL) + IDirectInput_Release(dinput); + dinput = NULL; +} + + +//============================================================ +// dinput_set_dword_property +//============================================================ + +static HRESULT dinput_set_dword_property(LPDIRECTINPUTDEVICE device, REFGUID property_guid, DWORD object, DWORD how, DWORD value) +{ + DIPROPDWORD dipdw; + + dipdw.diph.dwSize = sizeof(dipdw); + dipdw.diph.dwHeaderSize = sizeof(dipdw.diph); + dipdw.diph.dwObj = object; + dipdw.diph.dwHow = how; + dipdw.dwData = value; + + return IDirectInputDevice_SetProperty(device, property_guid, &dipdw.diph); +} + + +//============================================================ +// dinput_device_create +//============================================================ + +static device_info *dinput_device_create(device_info **devlist_head_ptr, LPCDIDEVICEINSTANCE instance, LPCDIDATAFORMAT format1, LPCDIDATAFORMAT format2, DWORD cooperative_level) +{ + device_info *devinfo; + HRESULT result; + + // allocate memory for the device object + devinfo = generic_device_alloc(devlist_head_ptr, instance->tszInstanceName); + + // attempt to create a device + result = IDirectInput_CreateDevice(dinput, &instance->guidInstance, &devinfo->dinput.device, NULL); + if (result != DI_OK) + goto error; + + // try to get a version 2 device for it + result = IDirectInputDevice_QueryInterface(devinfo->dinput.device, &IID_IDirectInputDevice2, (void **)&devinfo->dinput.device2); + if (result != DI_OK) + devinfo->dinput.device2 = NULL; + + // get the caps + devinfo->dinput.caps.dwSize = STRUCTSIZE(DIDEVCAPS); + result = IDirectInputDevice_GetCapabilities(devinfo->dinput.device, &devinfo->dinput.caps); + if (result != DI_OK) + goto error; + + // attempt to set the data format + devinfo->dinput.format = format1; + result = IDirectInputDevice_SetDataFormat(devinfo->dinput.device, devinfo->dinput.format); + if (result != DI_OK) + { + // use the secondary format if available + if (format2 != NULL) + { + devinfo->dinput.format = format2; + result = IDirectInputDevice_SetDataFormat(devinfo->dinput.device, devinfo->dinput.format); + } + if (result != DI_OK) + goto error; + } + + // set the cooperative level + result = IDirectInputDevice_SetCooperativeLevel(devinfo->dinput.device, win_window_list->hwnd, cooperative_level); + if (result != DI_OK) + goto error; + return devinfo; + +error: + dinput_device_release(devinfo); + return NULL; +} + + +//============================================================ +// dinput_device_release +//============================================================ + +static void dinput_device_release(device_info *devinfo) +{ + // release the version 2 device if present + if (devinfo->dinput.device2 != NULL) + IDirectInputDevice_Release(devinfo->dinput.device2); + devinfo->dinput.device2 = NULL; + + // release the regular device if present + if (devinfo->dinput.device != NULL) + IDirectInputDevice_Release(devinfo->dinput.device); + devinfo->dinput.device = NULL; + + // free the item list + generic_device_free(devinfo); +} + + +//============================================================ +// dinput_device_item_name +//============================================================ + +static const char *dinput_device_item_name(device_info *devinfo, int offset, const TCHAR *defstring, const TCHAR *suffix) +{ + DIDEVICEOBJECTINSTANCE instance = { 0 }; + const TCHAR *namestring = instance.tszName; + TCHAR *combined; + HRESULT result; + char *utf8; + + // query the key name + instance.dwSize = STRUCTSIZE(DIDEVICEOBJECTINSTANCE); + result = IDirectInputDevice_GetObjectInfo(devinfo->dinput.device, &instance, offset, DIPH_BYOFFSET); + + // if we got an error and have no default string, just return NULL + if (result != DI_OK) + { + if (defstring == NULL) + return NULL; + namestring = defstring; + } + + // if no suffix, return as-is + if (suffix == NULL) + return utf8_from_tstring(namestring); + + // otherwise, allocate space to add the suffix + combined = malloc_or_die(_tcslen(namestring) + 1 + _tcslen(suffix) + 1); + _tcscpy(combined, namestring); + _tcscat(combined, TEXT(" ")); + _tcscat(combined, suffix); + + // convert to UTF8, free the temporary string, and return + utf8 = utf8_from_tstring(combined); + free(combined); + return utf8; +} + + +//============================================================ +// dinput_device_poll +//============================================================ + +static HRESULT dinput_device_poll(device_info *devinfo) +{ + HRESULT result; + + // first poll the device, then get the state + if (devinfo->dinput.device2 != NULL) + IDirectInputDevice2_Poll(devinfo->dinput.device2); + result = IDirectInputDevice_GetDeviceState(devinfo->dinput.device, devinfo->dinput.format->dwDataSize, &devinfo->joystick.state); + + // handle lost inputs here + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) + { + result = IDirectInputDevice_Acquire(devinfo->dinput.device); + if (result == DI_OK) + result = IDirectInputDevice_GetDeviceState(devinfo->dinput.device, devinfo->dinput.format->dwDataSize, &devinfo->joystick.state); + } + + return result; +} + + +//============================================================ +// dinput_keyboard_enum +//============================================================ + +static BOOL CALLBACK dinput_keyboard_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref) +{ + device_info *devinfo; + int keynum; + + // allocate and link in a new device + devinfo = dinput_device_create(&keyboard_list, instance, &c_dfDIKeyboard, NULL, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); + if (devinfo == NULL) + goto exit; + + // add the device + devinfo->device = input_device_add(DEVICE_CLASS_KEYBOARD, devinfo->name, devinfo); + devinfo->poll = dinput_keyboard_poll; + + // populate it + for (keynum = 0; keynum < MAX_KEYS; keynum++) + { + input_item_id itemid = keyboard_map_scancode_to_itemid(keynum); + TCHAR defname[20]; + const char *name; + + // generate/fetch the name + _sntprintf(defname, ARRAY_LENGTH(defname), TEXT("Scan%03d"), keynum); + name = dinput_device_item_name(devinfo, keynum, defname, NULL); + + // add the item to the device + input_device_item_add(devinfo->device, name, &devinfo->keyboard.state[keynum], itemid, generic_button_get_state); + free((void *)name); + } + +exit: + return DIENUM_CONTINUE; +} + + +//============================================================ +// dinput_keyboard_poll +//============================================================ + +static void dinput_keyboard_poll(device_info *devinfo) +{ + HRESULT result = dinput_device_poll(devinfo); + + // for the first device, if we errored, or if we previously reported win32 keys, + // then ignore the dinput state and poll using win32 + if (devinfo == keyboard_list && (result != DI_OK || keyboard_win32_reported_key_down)) + win32_keyboard_poll(devinfo); +} + + +//============================================================ +// dinput_mouse_enum +//============================================================ + +static BOOL CALLBACK dinput_mouse_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref) +{ + device_info *devinfo, *guninfo = NULL; + int axisnum, butnum; + HRESULT result; + + // allocate and link in a new device + devinfo = dinput_device_create(&mouse_list, instance, &c_dfDIMouse2, &c_dfDIMouse, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); + if (devinfo == NULL) + goto exit; + + // allocate a second device for the gun (unless we are using the shared axis mode) + // we only support a single gun in dinput mode, so only add one + if (!lightgun_shared_axis_mode && devinfo == mouse_list) + { + guninfo = generic_device_alloc(&lightgun_list, instance->tszInstanceName); + if (guninfo == NULL) + goto exit; + } + + // set relative mode on the mouse device + result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_REL); + if (result != DI_OK) + { + mame_printf_error("DirectInput: Unable to set relative mode for mouse %d (%s)\n", generic_device_index(mouse_list, devinfo), devinfo->name); + goto error; + } + + // add the device + devinfo->device = input_device_add(DEVICE_CLASS_MOUSE, devinfo->name, devinfo); + devinfo->poll = dinput_mouse_poll; + if (guninfo != NULL) + { + guninfo->device = input_device_add(DEVICE_CLASS_LIGHTGUN, guninfo->name, guninfo); + guninfo->poll = win32_lightgun_poll; + } + + // cap the number of axes and buttons based on the format + devinfo->dinput.caps.dwAxes = MIN(devinfo->dinput.caps.dwAxes, 3); + devinfo->dinput.caps.dwButtons = MIN(devinfo->dinput.caps.dwButtons, (devinfo->dinput.format == &c_dfDIMouse) ? 4 : 8); + + // populate the axes + for (axisnum = 0; axisnum < devinfo->dinput.caps.dwAxes; axisnum++) + { + const char *name = dinput_device_item_name(devinfo, offsetof(DIMOUSESTATE, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], NULL); + + // add to the mouse device and optionally to the gun device as well + input_device_item_add(devinfo->device, name, &devinfo->mouse.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state); + if (guninfo != NULL && axisnum < 2) + input_device_item_add(guninfo->device, name, &guninfo->mouse.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state); + + free((void *)name); + } + + // populate the buttons + for (butnum = 0; butnum < devinfo->dinput.caps.dwButtons; butnum++) + { + const char *name = dinput_device_item_name(devinfo, offsetof(DIMOUSESTATE, rgbButtons[butnum]), default_button_name(butnum), NULL); + + // add to the mouse device and optionally to the gun device as well + // note that the gun device points to the mouse buttons rather than its own + input_device_item_add(devinfo->device, name, &devinfo->mouse.state.rgbButtons[butnum], ITEM_ID_BUTTON1 + butnum, generic_button_get_state); + if (guninfo != NULL) + input_device_item_add(guninfo->device, name, &devinfo->mouse.state.rgbButtons[butnum], ITEM_ID_BUTTON1 + butnum, generic_button_get_state); + + free((void *)name); + } + +exit: + return DIENUM_CONTINUE; + +error: + if (guninfo != NULL) + generic_device_free(guninfo); + if (devinfo != NULL) + dinput_device_release(devinfo); + goto exit; +} + + +//============================================================ +// dinput_mouse_poll +//============================================================ + +static void dinput_mouse_poll(device_info *devinfo) +{ + // poll + dinput_device_poll(devinfo); + + // scale the axis data + devinfo->mouse.state.lX *= INPUT_RELATIVE_PER_PIXEL; + devinfo->mouse.state.lY *= INPUT_RELATIVE_PER_PIXEL; + devinfo->mouse.state.lZ *= INPUT_RELATIVE_PER_PIXEL; +} + + +//============================================================ +// dinput_joystick_enum +//============================================================ + +static BOOL CALLBACK dinput_joystick_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref) +{ + DWORD cooperative_level = (HAS_WINDOW_MENU ? DISCL_BACKGROUND : DISCL_FOREGROUND) | DISCL_EXCLUSIVE; + int axisnum, axiscount, povnum, butnum; + device_info *devinfo; + HRESULT result; + + // allocate and link in a new device + devinfo = dinput_device_create(&joystick_list, instance, &c_dfDIJoystick, NULL, cooperative_level); + if (devinfo == NULL) + goto exit; + + // set absolute mode + result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_ABS); + if (result != DI_OK) + mame_printf_warning("DirectInput: Unable to set absolute mode for joystick %d (%s)\n", generic_device_index(joystick_list, devinfo), devinfo->name); + + // turn off deadzone; we do our own calculations + result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_DEADZONE, 0, DIPH_DEVICE, 0); + if (result != DI_OK) + mame_printf_warning("DirectInput: Unable to reset deadzone for joystick %d (%s)\n", generic_device_index(joystick_list, devinfo), devinfo->name); + + // turn off saturation; we do our own calculations + result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_SATURATION, 0, DIPH_DEVICE, 10000); + if (result != DI_OK) + mame_printf_warning("DirectInput: Unable to reset saturation for joystick %d (%s)\n", generic_device_index(joystick_list, devinfo), devinfo->name); + + // cap the number of axes, POVs, and buttons based on the format + devinfo->dinput.caps.dwAxes = MIN(devinfo->dinput.caps.dwAxes, 8); + devinfo->dinput.caps.dwPOVs = MIN(devinfo->dinput.caps.dwPOVs, 4); + devinfo->dinput.caps.dwButtons = MIN(devinfo->dinput.caps.dwButtons, 128); + + // add the device + devinfo->device = input_device_add(DEVICE_CLASS_JOYSTICK, devinfo->name, devinfo); + devinfo->poll = dinput_joystick_poll; + + // populate the axes + for (axisnum = axiscount = 0; axiscount < devinfo->dinput.caps.dwAxes && axisnum < 8; axisnum++) + { + DIPROPRANGE dipr; + const char *name; + + // fetch the range of this axis + dipr.diph.dwSize = sizeof(dipr); + dipr.diph.dwHeaderSize = sizeof(dipr.diph); + dipr.diph.dwObj = offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG); + dipr.diph.dwHow = DIPH_BYOFFSET; + result = IDirectInputDevice_GetProperty(devinfo->dinput.device, DIPROP_RANGE, &dipr.diph); + if (result != DI_OK) + continue; + devinfo->joystick.rangemin[axisnum] = dipr.lMin; + devinfo->joystick.rangemax[axisnum] = dipr.lMax; + + // populate the item description as well + name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], NULL); + input_device_item_add(devinfo->device, name, &devinfo->joystick.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state); + free((void *)name); + + axiscount++; + } + + // populate the POVs + for (povnum = 0; povnum < devinfo->dinput.caps.dwPOVs; povnum++) + { + const char *name; + + // left + name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("L")); + input_device_item_add(devinfo->device, name, (void *)(FPTR)(povnum * 4 + POVDIR_LEFT), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state); + free((void *)name); + + // right + name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("R")); + input_device_item_add(devinfo->device, name, (void *)(FPTR)(povnum * 4 + POVDIR_RIGHT), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state); + free((void *)name); + + // up + name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("U")); + input_device_item_add(devinfo->device, name, (void *)(FPTR)(povnum * 4 + POVDIR_UP), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state); + free((void *)name); + + // down + name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("D")); + input_device_item_add(devinfo->device, name, (void *)(FPTR)(povnum * 4 + POVDIR_DOWN), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state); + free((void *)name); + } + + // populate the buttons + for (butnum = 0; butnum < devinfo->dinput.caps.dwButtons; butnum++) + { + const char *name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgbButtons[butnum]), default_button_name(butnum), NULL); + input_device_item_add(devinfo->device, name, &devinfo->joystick.state.rgbButtons[butnum], (butnum < 16) ? (ITEM_ID_BUTTON1 + butnum) : ITEM_ID_OTHER_SWITCH, generic_button_get_state); + free((void *)name); + } + +exit: + return DIENUM_CONTINUE; +} + + +//============================================================ +// dinput_joystick_poll +//============================================================ + +static void dinput_joystick_poll(device_info *devinfo) +{ + int axisnum; + + // poll the device first + dinput_device_poll(devinfo); + + // normalize axis values + for (axisnum = 0; axisnum < 8; axisnum++) + { + LONG *axis = (&devinfo->joystick.state.lX) + axisnum; + *axis = normalize_absolute_axis(*axis, devinfo->joystick.rangemin[axisnum], devinfo->joystick.rangemax[axisnum]); + } +} + + +//============================================================ +// dinput_joystick_pov_get_state +//============================================================ + +static INT32 dinput_joystick_pov_get_state(void *device_internal, void *item_internal) +{ + device_info *devinfo = device_internal; + int povnum = (FPTR)item_internal / 4; + int povdir = (FPTR)item_internal % 4; + INT32 result = 0; + DWORD pov; + + // get the current state + poll_if_necessary(); + pov = devinfo->joystick.state.rgdwPOV[povnum]; + + // if invalid, return 0 + if ((pov & 0xffff) == 0xffff) + return result; + + // return the current state + switch (povdir) + { + case POVDIR_LEFT: result = (pov >= 22500 && pov <= 31500); break; + case POVDIR_RIGHT: result = (pov >= 4500 && pov <= 13500); break; + case POVDIR_UP: result = (pov >= 31500 || pov <= 4500); break; + case POVDIR_DOWN: result = (pov >= 13500 && pov <= 22500); break; + } + return result; +} + + +//============================================================ +// rawinput_init +//============================================================ + +static void rawinput_init(running_machine *machine) +{ + RAWINPUTDEVICELIST *devlist = NULL; + int device_count, devnum, regcount; + RAWINPUTDEVICE reglist[2]; + HMODULE user32; + + // we need pause and exit callbacks + add_exit_callback(machine, rawinput_exit); + + // look in user32 for the raw input APIs + user32 = LoadLibrary(TEXT("user32.dll")); + if (user32 == NULL) + goto error; + + // look up the entry points + register_rawinput_devices = (register_rawinput_devices_ptr)GetProcAddress(user32, "RegisterRawInputDevices"); + get_rawinput_device_list = (get_rawinput_device_list_ptr)GetProcAddress(user32, "GetRawInputDeviceList"); + get_rawinput_device_info = (get_rawinput_device_info_ptr)GetProcAddress(user32, "GetRawInputDeviceInfo" UNICODE_SUFFIX); + get_rawinput_data = (get_rawinput_data_ptr)GetProcAddress(user32, "GetRawInputData"); + if (register_rawinput_devices == NULL || get_rawinput_device_list == NULL || get_rawinput_device_info == NULL || get_rawinput_data == NULL) + goto error; + mame_printf_verbose("RawInput: APIs detected\n"); + + // get the number of devices, allocate a device list, and fetch it + if ((*get_rawinput_device_list)(NULL, &device_count, sizeof(*devlist)) != 0) + goto error; + if (device_count == 0) + goto error; + devlist = malloc_or_die(device_count * sizeof(*devlist)); + if ((*get_rawinput_device_list)(devlist, &device_count, sizeof(*devlist)) == -1) + goto error; + + // iterate backwards through devices; new devices are added at the head + for (devnum = device_count - 1; devnum >= 0; devnum--) + { + RAWINPUTDEVICELIST *device = &devlist[devnum]; + + // handle keyboards + if (device->dwType == RIM_TYPEKEYBOARD && !FORCE_DIRECTINPUT) + rawinput_keyboard_enum(device); + + // handle mice + else if (device->dwType == RIM_TYPEMOUSE && !FORCE_DIRECTINPUT) + rawinput_mouse_enum(device); + } + + // finally, register to recieve raw input WM_INPUT messages + regcount = 0; + if (keyboard_list != NULL) + { + reglist[regcount].usUsagePage = 0x01; + reglist[regcount].usUsage = 0x06; + reglist[regcount].dwFlags = RIDEV_INPUTSINK; + reglist[regcount].hwndTarget = win_window_list->hwnd; + regcount++; + } + if (mouse_list != NULL) + { + reglist[regcount].usUsagePage = 0x01; + reglist[regcount].usUsage = 0x02; + reglist[regcount].dwFlags = 0; + reglist[regcount].hwndTarget = win_window_list->hwnd; + regcount++; + } + + // if the registration fails, we need to back off + if (regcount > 0) + if (!(*register_rawinput_devices)(reglist, regcount, sizeof(reglist[0]))) + goto error; + + free(devlist); + return; + +error: + if (devlist != NULL) + free(devlist); +} + + +//============================================================ +// rawinput_exit +//============================================================ + +static void rawinput_exit(running_machine *machine) +{ + // release all our devices + while (lightgun_list != NULL && lightgun_list->rawinput.device != NULL) + rawinput_device_release(lightgun_list); + while (mouse_list != NULL && mouse_list->rawinput.device != NULL) + rawinput_device_release(mouse_list); + while (keyboard_list != NULL && keyboard_list->rawinput.device != NULL) + rawinput_device_release(keyboard_list); +} + + +//============================================================ +// rawinput_device_create +//============================================================ + +static device_info *rawinput_device_create(device_info **devlist_head_ptr, PRAWINPUTDEVICELIST device) +{ + device_info *devinfo = NULL; + TCHAR *tname = NULL; + INT name_length; + + // determine the length of the device name, allocate it, and fetch it + if ((*get_rawinput_device_info)(device->hDevice, RIDI_DEVICENAME, NULL, &name_length) != 0) + goto error; + tname = malloc_or_die(name_length * sizeof(*tname)); + if ((*get_rawinput_device_info)(device->hDevice, RIDI_DEVICENAME, tname, &name_length) == -1) + goto error; + + // if this is an RDP name, skip it + if (_tcsstr(tname, TEXT("Root#RDP_")) != NULL) + goto error; + + // improve the name and then allocate a device + tname = rawinput_device_improve_name(tname); + devinfo = generic_device_alloc(devlist_head_ptr, tname); + free(tname); + + // copy the handle + devinfo->rawinput.device = device->hDevice; + return devinfo; + +error: + if (tname != NULL) + free(tname); + if (devinfo != NULL) + rawinput_device_release(devinfo); + return NULL; +} + + +//============================================================ +// rawinput_device_release +//============================================================ + +static void rawinput_device_release(device_info *devinfo) +{ + // free the item list + generic_device_free(devinfo); +} + + +//============================================================ +// rawinput_device_name +//============================================================ + +static TCHAR *rawinput_device_improve_name(TCHAR *name) +{ + static const TCHAR *usbbasepath = TEXT("SYSTEM\\CurrentControlSet\\Enum\\USB"); + static const TCHAR *basepath = TEXT("SYSTEM\\CurrentControlSet\\Enum\\"); + TCHAR *regstring = NULL; + TCHAR *parentid = NULL; + TCHAR *regpath = NULL; + const TCHAR *chsrc; + HKEY regkey = NULL; + int usbindex; + TCHAR *chdst; + LONG result; + + // ensure the name is something we can handle + if (_tcsncmp(name, TEXT("\\\\?\\"), 4) != 0) + return name; + + // allocate a temporary string and concatenate the base path plus the name + regpath = malloc_or_die(sizeof(*regpath) * (_tcslen(basepath) + 1 + _tcslen(name))); + _tcscpy(regpath, basepath); + chdst = regpath + _tcslen(regpath); + + // convert all # to \ in the name + for (chsrc = name + 4; *chsrc != 0; chsrc++) + *chdst++ = (*chsrc == '#') ? '\\' : *chsrc; + *chdst = 0; + + // remove the final chunk + chdst = _tcsrchr(regpath, '\\'); + if (chdst == NULL) + goto exit; + *chdst = 0; + + // now try to open the registry key + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, ®key); + if (result != ERROR_SUCCESS) + goto exit; + + // fetch the device description; if it exists, we are finished + regstring = reg_query_string(regkey, TEXT("DeviceDesc")); + if (regstring != NULL) + goto convert; + + // close this key + RegCloseKey(regkey); + regkey = NULL; + + // if the key name does not contain "HID", it's not going to be in the USB tree; give up + if (_tcsstr(regpath, TEXT("HID")) == NULL) + goto exit; + + // extract the expected parent ID from the regpath + parentid = _tcsrchr(regpath, '\\'); + if (parentid == NULL) + goto exit; + parentid++; + + // open the USB key + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, usbbasepath, 0, KEY_READ, ®key); + if (result != ERROR_SUCCESS) + goto exit; + + // enumerate the USB key + for (usbindex = 0; result == ERROR_SUCCESS && regstring == NULL; usbindex++) + { + TCHAR keyname[MAX_PATH]; + DWORD namelen; + + // get the next enumerated subkey and scan it + namelen = ARRAY_LENGTH(keyname) - 1; + result = RegEnumKeyEx(regkey, usbindex, keyname, &namelen, NULL, NULL, NULL, NULL); + if (result == ERROR_SUCCESS) + { + LONG subresult; + int subindex; + HKEY subkey; + + // open the subkey + subresult = RegOpenKeyEx(regkey, keyname, 0, KEY_READ, &subkey); + if (subresult != ERROR_SUCCESS) + continue; + + // enumerate the subkey + for (subindex = 0; subresult == ERROR_SUCCESS && regstring == NULL; subindex++) + { + // get the next enumerated subkey and scan it + namelen = ARRAY_LENGTH(keyname) - 1; + subresult = RegEnumKeyEx(subkey, subindex, keyname, &namelen, NULL, NULL, NULL, NULL); + if (subresult == ERROR_SUCCESS) + { + TCHAR *endparentid; + LONG endresult; + HKEY endkey; + + // open this final key + endresult = RegOpenKeyEx(subkey, keyname, 0, KEY_READ, &endkey); + if (endresult != ERROR_SUCCESS) + continue; + + // do we have a match? + endparentid = reg_query_string(endkey, TEXT("ParentIdPrefix")); + if (endparentid != NULL && _tcsncmp(parentid, endparentid, _tcslen(endparentid)) == 0) + regstring = reg_query_string(endkey, TEXT("DeviceDesc")); + + // free memory and close the key + if (endparentid != NULL) + free(endparentid); + RegCloseKey(endkey); + } + } + + // close the subkey + RegCloseKey(subkey); + } + } + + // if we didn't find anything, go to the exit + if (regstring == NULL) + goto exit; + +convert: + // replace the name with the nicer one + free(name); + + // remove anything prior to the final semicolon + chsrc = _tcsrchr(regstring, ';'); + if (chsrc != NULL) + chsrc++; + else + chsrc = regstring; + name = malloc_or_die(sizeof(*name) * (_tcslen(chsrc) + 1)); + _tcscpy(name, chsrc); + +exit: + if (regstring != NULL) + free(regstring); + if (regpath != NULL) + free(regpath); + if (regkey != NULL) + RegCloseKey(regkey); + + return name; +} + + +//============================================================ +// rawinput_keyboard_enum +//============================================================ + +static void rawinput_keyboard_enum(PRAWINPUTDEVICELIST device) +{ + device_info *devinfo; + int keynum; + + // allocate and link in a new device + devinfo = rawinput_device_create(&keyboard_list, device); + if (devinfo == NULL) + return; + + // add the device + devinfo->device = input_device_add(DEVICE_CLASS_KEYBOARD, devinfo->name, devinfo); + + // populate it + for (keynum = 0; keynum < MAX_KEYS; keynum++) + { + input_item_id itemid = keyboard_map_scancode_to_itemid(keynum); + TCHAR keyname[100]; + const char *name; + + // generate the name + if (GetKeyNameText(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, ARRAY_LENGTH(keyname)) == 0) + _sntprintf(keyname, ARRAY_LENGTH(keyname), TEXT("Scan%03d"), keynum); + name = utf8_from_tstring(keyname); + + // add the item to the device + input_device_item_add(devinfo->device, name, &devinfo->keyboard.state[keynum], itemid, generic_button_get_state); + free((void *)name); + } +} + + +//============================================================ +// rawinput_keyboard_update +//============================================================ + +static void rawinput_keyboard_update(HANDLE device, RAWKEYBOARD *data) +{ + device_info *devinfo; + + // find the keyboard in the list and process + for (devinfo = keyboard_list; devinfo != NULL; devinfo = devinfo->next) + if (devinfo->rawinput.device == device) + { + // determine the full DIK-compatible scancode + UINT8 scancode = (data->MakeCode & 0x7f) | ((data->Flags & RI_KEY_E0) ? 0x80 : 0x00); + + // scancode 0xaa is a special shift code we need to ignore + if (scancode == 0xaa) + break; + + // set or clear the key + if (!(data->Flags & RI_KEY_BREAK)) + devinfo->keyboard.state[scancode] = 0x80; + else + devinfo->keyboard.state[scancode] = 0x00; + break; + } +} + + +//============================================================ +// rawinput_mouse_enum +//============================================================ + +static void rawinput_mouse_enum(PRAWINPUTDEVICELIST device) +{ + device_info *devinfo, *guninfo = NULL; + int axisnum, butnum; + + // allocate and link in a new mouse device + devinfo = rawinput_device_create(&mouse_list, device); + if (devinfo == NULL) + return; + devinfo->poll = rawinput_mouse_poll; + + // allocate a second device for the gun (unless we are using the shared axis mode) + if (!lightgun_shared_axis_mode) + { + guninfo = rawinput_device_create(&lightgun_list, device); + assert(guninfo != NULL); + } + + // add the device + devinfo->device = input_device_add(DEVICE_CLASS_MOUSE, devinfo->name, devinfo); + if (guninfo != NULL) + { + guninfo->device = input_device_add(DEVICE_CLASS_LIGHTGUN, guninfo->name, guninfo); + guninfo->poll = (guninfo == lightgun_list) ? win32_lightgun_poll : NULL; + guninfo->mouse.partner = &devinfo->mouse; + } + + // populate the axes + for (axisnum = 0; axisnum < 3; axisnum++) + { + const char *name = utf8_from_tstring(default_axis_name[axisnum]); + + // add to the mouse device and optionally to the gun device as well + input_device_item_add(devinfo->device, name, &devinfo->mouse.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state); + if (guninfo != NULL && axisnum < 2) + input_device_item_add(guninfo->device, name, &guninfo->mouse.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state); + + free((void *)name); + } + + // populate the buttons + for (butnum = 0; butnum < 5; butnum++) + { + const char *name = utf8_from_tstring(default_button_name(butnum)); + + // add to the mouse device and optionally to the gun device as well + // note that the gun device points to the mouse buttons rather than its own + input_device_item_add(devinfo->device, name, &devinfo->mouse.state.rgbButtons[butnum], ITEM_ID_BUTTON1 + butnum, generic_button_get_state); + if (guninfo != NULL) + input_device_item_add(guninfo->device, name, &devinfo->mouse.state.rgbButtons[butnum], ITEM_ID_BUTTON1 + butnum, generic_button_get_state); + + free((void *)name); + } +} + + +//============================================================ +// rawinput_mouse_update +//============================================================ + +static void rawinput_mouse_update(HANDLE device, RAWMOUSE *data) +{ + device_info *devlist = (data->usFlags & MOUSE_MOVE_ABSOLUTE) ? lightgun_list : mouse_list; + device_info *devinfo; + + // find the mouse in the list and process + for (devinfo = devlist; devinfo != NULL; devinfo = devinfo->next) + if (devinfo->rawinput.device == device) + { + mouse_state *mouseinfo = (devinfo->mouse.partner != NULL) ? devinfo->mouse.partner : &devinfo->mouse; + + // if we got relative data, update it as a mouse + if (!(data->usFlags & MOUSE_MOVE_ABSOLUTE)) + { + devinfo->mouse.raw_x += data->lLastX * INPUT_RELATIVE_PER_PIXEL; + devinfo->mouse.raw_y += data->lLastY * INPUT_RELATIVE_PER_PIXEL; + + // update zaxis + if (data->usButtonFlags & RI_MOUSE_WHEEL) + devinfo->mouse.raw_z += (INT16)data->usButtonData * INPUT_RELATIVE_PER_PIXEL; + } + + // otherwise, update it as a lightgun + else + { + devinfo->mouse.raw_x = normalize_absolute_axis(data->lLastX, 0, 0xffff); + devinfo->mouse.raw_y = normalize_absolute_axis(data->lLastY, 0, 0xffff); + + // also clear the polling function so win32_lightgun_poll isn't called + devinfo->poll = NULL; + } + + // update the button states; always update the corresponding mouse buttons + if (data->usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) mouseinfo->state.rgbButtons[0] = 0x80; + if (data->usButtonFlags & RI_MOUSE_BUTTON_1_UP) mouseinfo->state.rgbButtons[0] = 0x00; + if (data->usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) mouseinfo->state.rgbButtons[1] = 0x80; + if (data->usButtonFlags & RI_MOUSE_BUTTON_2_UP) mouseinfo->state.rgbButtons[1] = 0x00; + if (data->usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) mouseinfo->state.rgbButtons[2] = 0x80; + if (data->usButtonFlags & RI_MOUSE_BUTTON_3_UP) mouseinfo->state.rgbButtons[2] = 0x00; + if (data->usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) mouseinfo->state.rgbButtons[3] = 0x80; + if (data->usButtonFlags & RI_MOUSE_BUTTON_4_UP) mouseinfo->state.rgbButtons[3] = 0x00; + if (data->usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) mouseinfo->state.rgbButtons[4] = 0x80; + if (data->usButtonFlags & RI_MOUSE_BUTTON_5_UP) mouseinfo->state.rgbButtons[4] = 0x00; + break; + } +} + + +//============================================================ +// rawinput_mouse_poll +//============================================================ + +static void rawinput_mouse_poll(device_info *devinfo) +{ + poll_if_necessary(); + + // copy the accumulated raw state to the actual state + osd_lock_acquire(input_lock); + devinfo->mouse.state.lX = devinfo->mouse.raw_x; + devinfo->mouse.state.lY = devinfo->mouse.raw_y; + devinfo->mouse.state.lZ = devinfo->mouse.raw_z; + devinfo->mouse.raw_x = 0; + devinfo->mouse.raw_y = 0; + devinfo->mouse.raw_z = 0; + osd_lock_release(input_lock); +} + + +//============================================================ +// reg_query_string +//============================================================ + +static TCHAR *reg_query_string(HKEY key, const TCHAR *path) +{ + TCHAR *buffer; + DWORD datalen; + LONG result; + + // first query to get the length + result = RegQueryValueEx(key, path, NULL, NULL, NULL, &datalen); + if (result != ERROR_SUCCESS) + return NULL; + + // allocate a buffer + buffer = malloc_or_die(datalen + sizeof(*buffer)); + buffer[datalen / sizeof(*buffer)] = 0; + + // now get the actual data + result = RegQueryValueEx(key, path, NULL, NULL, (LPBYTE)buffer, &datalen); + if (result == ERROR_SUCCESS) + return buffer; + + // otherwise return a NULL buffer + free(buffer); + return NULL; +} + + +//============================================================ +// default_button_name +//============================================================ + +static const TCHAR *default_button_name(int which) +{ + static TCHAR buffer[20]; + _sntprintf(buffer, ARRAY_LENGTH(buffer), TEXT("B%d"), which); + return buffer; +} + + +//============================================================ +// default_pov_name +//============================================================ + +static const TCHAR *default_pov_name(int which) +{ + static TCHAR buffer[20]; + _sntprintf(buffer, ARRAY_LENGTH(buffer), TEXT("POV%d"), which); + return buffer; +} diff --git a/src/osd/windows/input.h b/src/osd/windows/input.h new file mode 100644 index 00000000000..a05c8ca05a8 --- /dev/null +++ b/src/osd/windows/input.h @@ -0,0 +1,27 @@ +//============================================================ +// +// input.h - Win32 implementation of MAME input routines +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __INPUT_H +#define __INPUT_H + + +//============================================================ +// PROTOTYPES +//============================================================ + +void wininput_init(running_machine *machine); +void wininput_poll(void); + +void wininput_handle_mouse_button(int button, int down, int x, int y); +BOOL wininput_handle_raw(HANDLE device); + +int wininput_should_hide_mouse(void); + + +#endif /* __INPUT_H */ diff --git a/src/osd/windows/ledutil.c b/src/osd/windows/ledutil.c new file mode 100644 index 00000000000..c8caaaa01ae --- /dev/null +++ b/src/osd/windows/ledutil.c @@ -0,0 +1,738 @@ +//============================================================ +// +// ledutil.c - Win32 example code that tracks changing +// outputs and updates the keyboard LEDs in response +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ +// +// This is sample code. To use it as a starting point, you +// should do the following: +// +// 1. Change the CLIENT_ID define to something unique. +// +// 2. Change the WINDOW_CLASS and WINDOW_NAME defines to +// something unique. +// +// 3. Delete all the code from the >8 snip 8< comment and +// downward. +// +// 4. Implement the following functions: +// +// output_startup - called at app init time +// output_shutdown - called before the app exits +// output_mame_start - called when MAME starts +// output_mame_stop - called when MAME exits +// output_set_state - called whenever state changes +// +//============================================================ + +// Needed for RAW Input +#define _WIN32_WINNT 0x501 + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include + +// standard C headers +#include +#include +#include +#include + +// MAME output header file +typedef void running_machine; +#include "output.h" + + + +//============================================================ +// DEBUGGING +//============================================================ + +// note you need to compile as a console app to have any of +// these printfs show up +#define DEBUG_VERSION 0 + +#if DEBUG_VERSION +#define DEBUG_PRINTF(x) printf x +#else +#define DEBUG_PRINTF(x) +#endif + + +//============================================================ +// CONSTANTS +//============================================================ + +// unique client ID +#define CLIENT_ID (('M' << 24) | ('L' << 16) | ('E' << 8) | ('D' << 0)) + +// LED methods +#define LED_METHOD_PS2 0 +#define LED_METHOD_USB 1 +#define LED_METHOD_WIN9X 2 + +// window parameters +#define WINDOW_CLASS TEXT("LEDSample") +#define WINDOW_NAME TEXT("LEDSample") + +// window styles +#define WINDOW_STYLE WS_OVERLAPPEDWINDOW +#define WINDOW_STYLE_EX 0 + +// Define the keyboard indicators. +// (Definitions borrowed from ntddkbd.h) + +#define IOCTL_KEYBOARD_SET_INDICATORS CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0002, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_KEYBOARD_QUERY_TYPEMATIC CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0008, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_KEYBOARD_QUERY_INDICATORS CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define KEYBOARD_SCROLL_LOCK_ON 1 +#define KEYBOARD_NUM_LOCK_ON 2 +#define KEYBOARD_CAPS_LOCK_ON 4 + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef struct _KEYBOARD_INDICATOR_PARAMETERS +{ + USHORT UnitId; // Unit identifier. + USHORT LedFlags; // LED indicator state. +} KEYBOARD_INDICATOR_PARAMETERS, *PKEYBOARD_INDICATOR_PARAMETERS; + + +typedef struct _id_map_entry id_map_entry; +struct _id_map_entry +{ + id_map_entry * next; + const char * name; + WPARAM id; +}; + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +static int ledmethod; +static int original_state; +static int current_state; +static int pause_state; +static HANDLE hKbdDev; + +static HWND mame_target; +static HWND listener_hwnd; + +static id_map_entry * idmaplist; + +// message IDs +static UINT om_mame_start; +static UINT om_mame_stop; +static UINT om_mame_update_state; +static UINT om_mame_register_client; +static UINT om_mame_unregister_client; +static UINT om_mame_get_id_string; + + + +//============================================================ +// FUNCTION PROTOTYPES +//============================================================ + +static int create_window_class(void); +static LRESULT CALLBACK listener_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam); +static LRESULT handle_mame_start(WPARAM wparam, LPARAM lparam); +static LRESULT handle_mame_stop(WPARAM wparam, LPARAM lparam); +static LRESULT handle_copydata(WPARAM wparam, LPARAM lparam); +static void reset_id_to_outname_cache(void); +static const char *map_id_to_outname(WPARAM id); +static LRESULT handle_update_state(WPARAM wparam, LPARAM lparam); + +// these functions provide the meat +static void output_startup(const char *commandline); +static void output_mame_start(void); +static void output_set_state(const char *name, INT32 state); +static void output_mame_stop(void); +static void output_shutdown(void); + +static int led_get_state(void); +static void led_set_state(int state); + + +//============================================================ +// main +//============================================================ + +int utf8_main(int argc, char *argv[]) +{ + const char *arg = (argc > 1) ? argv[1] : ""; + int exitcode = 1; + HWND otherwnd; + MSG message; + int result; + + // see if there is another instance of us running + otherwnd = FindWindow(WINDOW_CLASS, WINDOW_NAME); + + // if the argument is "-kill", post a close message + if (strcmp(arg, "-kill") == 0) + { + if (otherwnd != NULL) + PostMessage(otherwnd, WM_QUIT, 0, 0); + return (otherwnd != NULL) ? 1 : 0; + } + + // if we had another instance, defer to it + if (otherwnd != NULL) + return 0; + + // call the startup code + output_startup(arg); + + // create our window class + result = create_window_class(); + if (result != 0) + goto error; + + // create a window + listener_hwnd = CreateWindowEx( + WINDOW_STYLE_EX, + WINDOW_CLASS, + WINDOW_NAME, + WINDOW_STYLE, + 0, 0, + 1, 1, + NULL, + NULL, + GetModuleHandle(NULL), + NULL); + if (listener_hwnd == NULL) + goto error; + + // allocate message ids + om_mame_start = RegisterWindowMessage(OM_MAME_START); + if (om_mame_start == 0) + goto error; + om_mame_stop = RegisterWindowMessage(OM_MAME_STOP); + if (om_mame_stop == 0) + goto error; + om_mame_update_state = RegisterWindowMessage(OM_MAME_UPDATE_STATE); + if (om_mame_update_state == 0) + goto error; + + om_mame_register_client = RegisterWindowMessage(OM_MAME_REGISTER_CLIENT); + if (om_mame_register_client == 0) + goto error; + om_mame_unregister_client = RegisterWindowMessage(OM_MAME_UNREGISTER_CLIENT); + if (om_mame_unregister_client == 0) + goto error; + om_mame_get_id_string = RegisterWindowMessage(OM_MAME_GET_ID_STRING); + if (om_mame_get_id_string == 0) + goto error; + + // see if MAME is already running + otherwnd = FindWindow(OUTPUT_WINDOW_CLASS, OUTPUT_WINDOW_NAME); + if (otherwnd != NULL) + handle_mame_start((WPARAM)otherwnd, 0); + + // process messages + while (GetMessage(&message, NULL, 0, 0)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + // reset on the way out if still live + if (mame_target != NULL) + handle_mame_stop((WPARAM)mame_target, 0); + exitcode = 0; + +error: + // call the shutdown code + output_shutdown(); + + return exitcode; +} + + +//============================================================ +// create_window_class +//============================================================ + +static int create_window_class(void) +{ + static int classes_created = FALSE; + + /* only do this once */ + if (!classes_created) + { + WNDCLASS wc = { 0 }; + + // initialize the description of the window class + wc.lpszClassName = WINDOW_CLASS; + wc.hInstance = GetModuleHandle(NULL); + wc.lpfnWndProc = listener_window_proc; + + // register the class; fail if we can't + if (!RegisterClass(&wc)) + return 1; + classes_created = TRUE; + } + + return 0; +} + + +//============================================================ +// window_proc +//============================================================ + +static LRESULT CALLBACK listener_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + // OM_MAME_START: register ourselves with the new MAME (first instance only) + if (message == om_mame_start) + return handle_mame_start(wparam, lparam); + + // OM_MAME_STOP: no need to unregister, just note that we've stopped caring and reset the LEDs + else if (message == om_mame_stop) + return handle_mame_stop(wparam, lparam); + + // OM_MAME_UPDATE_STATE: update the state of this item if we care + else if (message == om_mame_update_state) + return handle_update_state(wparam, lparam); + + // WM_COPYDATA: extract the string and create an ID map entry + else if (message == WM_COPYDATA) + return handle_copydata(wparam, lparam); + + // everything else is default + else + return DefWindowProc(wnd, message, wparam, lparam); +} + + +//============================================================ +// handle_mame_start +//============================================================ + +static LRESULT handle_mame_start(WPARAM wparam, LPARAM lparam) +{ + DEBUG_PRINTF(("mame_start (%08X)\n", (UINT32)wparam)); + + // make this the targeted version of MAME + mame_target = (HWND)wparam; + + // initialize the LED states + output_mame_start(); + reset_id_to_outname_cache(); + + // register ourselves as a client + PostMessage(mame_target, om_mame_register_client, (WPARAM)listener_hwnd, CLIENT_ID); + + // get the game name + map_id_to_outname(0); + return 0; +} + + +//============================================================ +// handle_mame_stop +//============================================================ + +static LRESULT handle_mame_stop(WPARAM wparam, LPARAM lparam) +{ + DEBUG_PRINTF(("mame_stop (%08X)\n", (UINT32)wparam)); + + // ignore if this is not the instance we care about + if (mame_target != (HWND)wparam) + return 1; + + // clear our target out + mame_target = NULL; + reset_id_to_outname_cache(); + + // reset the LED states + output_mame_stop(); + return 0; +} + + +//============================================================ +// handle_copydata +//============================================================ + +static LRESULT handle_copydata(WPARAM wparam, LPARAM lparam) +{ + COPYDATASTRUCT *copydata = (COPYDATASTRUCT *)lparam; + copydata_id_string *data = (copydata_id_string *)copydata->lpData; + id_map_entry *entry; + char *string; + + DEBUG_PRINTF(("copydata (%08X)\n", (UINT32)wparam)); + + // ignore requests we don't care about + if (mame_target != (HWND)wparam) + return 1; + + // allocate memory + entry = malloc(sizeof(*entry)); + string = malloc(strlen(data->string) + 1); + + // if all allocations worked, make a new entry + if (entry != NULL && string != NULL) + { + entry->next = idmaplist; + entry->name = string; + entry->id = data->id; + + // copy the string and hook us into the list + strcpy(string, data->string); + idmaplist = entry; + + DEBUG_PRINTF((" id %d = '%s'\n", entry->id, entry->name)); + } + + return 0; +} + + +//============================================================ +// reset_id_to_outname_cache +//============================================================ + +static void reset_id_to_outname_cache(void) +{ + // free our ID list + while (idmaplist != NULL) + { + id_map_entry *temp = idmaplist; + idmaplist = temp->next; + free(temp); + } +} + + +//============================================================ +// map_id_to_outname +//============================================================ + +static const char *map_id_to_outname(WPARAM id) +{ + id_map_entry *entry; + + // see if we have an entry in our map + for (entry = idmaplist; entry != NULL; entry = entry->next) + if (entry->id == id) + return entry->name; + + // no entry yet; we have to ask + SendMessage(mame_target, om_mame_get_id_string, (WPARAM)listener_hwnd, id); + + // now see if we have the entry in our map + for (entry = idmaplist; entry != NULL; entry = entry->next) + if (entry->id == id) + return entry->name; + + // if not, use an empty string + return ""; +} + + +//============================================================ +// handle_update_state +//============================================================ + +static LRESULT handle_update_state(WPARAM wparam, LPARAM lparam) +{ + DEBUG_PRINTF(("update_state: id=%d state=%d\n", (UINT32)wparam, (UINT32)lparam)); + output_set_state(map_id_to_outname(wparam), lparam); + return 0; +} + + +// +// END BOILERPLATE CODE +// +// ------------------------>8 snip 8<------------------------- +// +// BEGIN LED-SPECIFIC CODE +// + + +//============================================================ +// output_startup +//============================================================ + +static void output_startup(const char *commandline) +{ + OSVERSIONINFO osinfo = { sizeof(OSVERSIONINFO) }; + + // default to PS/2, override if USB is specified as a parameter + ledmethod = LED_METHOD_PS2; + if (commandline != NULL && strcmp(commandline, "-usb") == 0) + ledmethod = LED_METHOD_USB; + + // force Win9x method if we're on Win 9x + GetVersionEx(&osinfo); + if (osinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + ledmethod = LED_METHOD_WIN9X; + + // output the method + switch (ledmethod) + { + case LED_METHOD_PS2: + DEBUG_PRINTF(("Using PS/2 method\n")); + break; + + case LED_METHOD_USB: + DEBUG_PRINTF(("Using USB method\n")); + break; + + case LED_METHOD_WIN9X: + DEBUG_PRINTF(("Using Win9x method\n")); + break; + } +} + + +//============================================================ +// output_shutdown +//============================================================ + +static void output_shutdown(void) +{ + // nothing to do here +} + + +//============================================================ +// output_mame_start +//============================================================ + +static void output_mame_start(void) +{ + HRESULT error_number; + + // initialize the system based on the method + switch (ledmethod) + { + case LED_METHOD_PS2: + if (!DefineDosDevice(DDD_RAW_TARGET_PATH, TEXT("Kbd"), TEXT("\\Device\\KeyboardClass0"))) + { + error_number = GetLastError(); + fprintf(stderr, "Unable to open the keyboard device. (error %d)\n", (UINT32)error_number); + return; + } + + hKbdDev = CreateFile(TEXT("\\\\.\\Kbd"), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hKbdDev == INVALID_HANDLE_VALUE) + { + error_number = GetLastError(); + fprintf(stderr, "Unable to open the keyboard device. (error %d)\n", (UINT32)error_number); + return; + } + break; + } + + // remember the initial LED states + original_state = current_state = led_get_state(); +} + + +//============================================================ +// output_mame_stop +//============================================================ + +static void output_mame_stop(void) +{ + int error_number = 0; + + // restore the initial LED states + led_set_state(original_state); + + switch (ledmethod) + { + case LED_METHOD_PS2: + if (!DefineDosDevice(DDD_REMOVE_DEFINITION, TEXT("Kbd"), NULL)) + { + error_number = GetLastError(); + fprintf(stderr, "Unable to close the keyboard device. (error %d)\n", error_number); + return; + } + if (!CloseHandle(hKbdDev)) + { + error_number = GetLastError(); + fprintf(stderr, "Unable to close the keyboard device. (error %d)\n", error_number); + return; + } + break; + } +} + + +//============================================================ +// output_set_state +//============================================================ + +static void output_set_state(const char *outname, INT32 state) +{ + // look for pause state + if (strcmp(outname, "pause") == 0) + { + if (state) + { + pause_state = led_get_state(); + led_set_state(original_state); + } + else + { + original_state = led_get_state(); + led_set_state(pause_state); + } + } + // look for LED0/LED1/LED2 states and update accordingly + else if (strcmp(outname, "led0") == 0) + led_set_state((current_state & ~1) | (state & 1)); + else if (strcmp(outname, "led1") == 0) + led_set_state((current_state & ~2) | ((state & 1) << 1)); + else if (strcmp(outname, "led2") == 0) + led_set_state((current_state & ~4) | ((state & 1) << 2)); +} + + +//============================================================ +// led_get_state +//============================================================ + +static int led_get_state(void) +{ + int result = 0; + + switch (ledmethod) + { + case LED_METHOD_WIN9X: + case LED_METHOD_USB: + { + BYTE key_states[256]; + + // get the current state + GetKeyboardState(&key_states[0]); + + // set the numlock bit + result |= (key_states[VK_NUMLOCK] & 1); + result |= (key_states[VK_CAPITAL] & 1) << 1; + result |= (key_states[VK_SCROLL] & 1) << 2; + break; + } + + case LED_METHOD_PS2: + { + KEYBOARD_INDICATOR_PARAMETERS OutputBuffer; // Output buffer for DeviceIoControl + ULONG DataLength = sizeof(KEYBOARD_INDICATOR_PARAMETERS); + ULONG ReturnedLength; // Number of bytes returned in output buffer + + // Address first keyboard + OutputBuffer.UnitId = 0; + + DeviceIoControl(hKbdDev, IOCTL_KEYBOARD_QUERY_INDICATORS, + NULL, 0, + &OutputBuffer, DataLength, + &ReturnedLength, NULL); + + // Demangle lights to match 95/98 + if (OutputBuffer.LedFlags & KEYBOARD_NUM_LOCK_ON) result |= 0x1; + if (OutputBuffer.LedFlags & KEYBOARD_CAPS_LOCK_ON) result |= 0x2; + if (OutputBuffer.LedFlags & KEYBOARD_SCROLL_LOCK_ON) result |= 0x4; + break; + } + } + + return result; +} + + +//============================================================ +// led_set_state +//============================================================ + +static void led_set_state(int state) +{ + current_state = state; + + switch (ledmethod) + { + case LED_METHOD_WIN9X: + { + // thanks to Lee Taylor for the original version of this code + BYTE key_states[256]; + + // get the current state + GetKeyboardState(&key_states[0]); + + // mask states and set new states + key_states[VK_NUMLOCK] = (key_states[VK_NUMLOCK] & ~1) | ((state >> 0) & 1); + key_states[VK_CAPITAL] = (key_states[VK_CAPITAL] & ~1) | ((state >> 1) & 1); + key_states[VK_SCROLL] = (key_states[VK_SCROLL] & ~1) | ((state >> 2) & 1); + + SetKeyboardState(&key_states[0]); + break; + } + + case LED_METHOD_USB: + { + static const BYTE vk[3] = { VK_NUMLOCK, VK_CAPITAL, VK_SCROLL }; + BYTE keyState[256]; + int k; + + GetKeyboardState((LPBYTE)&keyState); + for (k = 0; k < 3; k++) + { + if ((((state >> k) & 1) && !(keyState[vk[k]] & 1)) || + (!((state >> k) & 1) && (keyState[vk[k]] & 1))) + { + // Simulate a key press + keybd_event(vk[k], 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0); + + // Simulate a key release + keybd_event(vk[k], 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); + } + } + + keyState[VK_NUMLOCK] = (keyState[VK_NUMLOCK] & ~1) | ((state >> 0) & 1); + keyState[VK_CAPITAL] = (keyState[VK_CAPITAL] & ~1) | ((state >> 1) & 1); + keyState[VK_SCROLL] = (keyState[VK_SCROLL] & ~1) | ((state >> 2) & 1); + SetKeyboardState(&keyState[0]); + break; + } + + case LED_METHOD_PS2: + { + KEYBOARD_INDICATOR_PARAMETERS InputBuffer; // Input buffer for DeviceIoControl + ULONG DataLength = sizeof(KEYBOARD_INDICATOR_PARAMETERS); + ULONG ReturnedLength; // Number of bytes returned in output buffer + UINT LedFlags=0; + + // Demangle lights to match 95/98 + if (state & 0x1) LedFlags |= KEYBOARD_NUM_LOCK_ON; + if (state & 0x2) LedFlags |= KEYBOARD_CAPS_LOCK_ON; + if (state & 0x4) LedFlags |= KEYBOARD_SCROLL_LOCK_ON; + + // Address first keyboard + InputBuffer.UnitId = 0; + InputBuffer.LedFlags = LedFlags; + DeviceIoControl(hKbdDev, IOCTL_KEYBOARD_SET_INDICATORS, + &InputBuffer, DataLength, + NULL, 0, + &ReturnedLength, NULL); + break; + } + } +} diff --git a/src/osd/windows/main.c b/src/osd/windows/main.c new file mode 100644 index 00000000000..0f4898b8104 --- /dev/null +++ b/src/osd/windows/main.c @@ -0,0 +1,89 @@ +//============================================================ +// +// main.c - Win32 main program +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define _WIN32_WINNT 0x0400 +#define WIN32_LEAN_AND_MEAN +#include +#include + +// MAMEOS headers +#include "strconv.h" + +extern int utf8_main(int argc, char **argv); + + + +//============================================================ +// main +//============================================================ + +// undo the command-line #define that maps main to utf8_main in all other cases +#ifndef WINUI +#undef main +#endif + +#ifdef __GNUC__ +int main(int argc, char **a_argv) +#else // !__GNUC__ +int _tmain(int argc, TCHAR **argv) +#endif // __GNUC__ +{ + int i, rc; + char **utf8_argv; + +#ifdef __GNUC__ + TCHAR **argv; +#ifdef UNICODE + // MinGW doesn't support wmain() directly, so we have to jump through some hoops + extern void __wgetmainargs(int *argc, wchar_t ***wargv, wchar_t ***wenviron, int expand_wildcards, int *startupinfo); + WCHAR **wenviron; + int startupinfo; + __wgetmainargs(&argc, &argv, &wenviron, 0, &startupinfo); +#else // !UNICODE + argv = a_argv; +#endif // UNICODE +#endif // __GNUC__ + +#ifdef MALLOC_DEBUG +{ + extern int winalloc_in_main_code; + winalloc_in_main_code = TRUE; +#endif + + /* convert arguments to UTF-8 */ + utf8_argv = (char **) malloc(argc * sizeof(*argv)); + if (utf8_argv == NULL) + return 999; + for (i = 0; i < argc; i++) + { + utf8_argv[i] = utf8_from_tstring(argv[i]); + if (utf8_argv[i] == NULL) + return 999; + } + + /* run utf8_main */ + rc = utf8_main(argc, utf8_argv); + + /* free arguments */ + for (i = 0; i < argc; i++) + free(utf8_argv[i]); + free(utf8_argv); + +#ifdef MALLOC_DEBUG + { + void check_unfreed_mem(void); + check_unfreed_mem(); + } + winalloc_in_main_code = FALSE; +} +#endif + + return rc; +} diff --git a/src/osd/windows/mame.man b/src/osd/windows/mame.man new file mode 100644 index 00000000000..2ea6a6b2e5a --- /dev/null +++ b/src/osd/windows/mame.man @@ -0,0 +1,10 @@ + + + + Multiple Arcade Machine Emulator + + + + + + diff --git a/src/osd/windows/mame.rc b/src/osd/windows/mame.rc new file mode 100644 index 00000000000..29ae8059256 --- /dev/null +++ b/src/osd/windows/mame.rc @@ -0,0 +1,13 @@ +// +// mame.rc - Minimal resource file for Win32 MAME +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// + +#define NOWINRES +#include "windows.h" +#include "commctrl.h" +#include "mamevers.rc" + +1 24 MOVEABLE PURE "mame.man" diff --git a/src/osd/windows/mamex64.man b/src/osd/windows/mamex64.man new file mode 100644 index 00000000000..cc62373447b --- /dev/null +++ b/src/osd/windows/mamex64.man @@ -0,0 +1,10 @@ + + + + Multiple Arcade Machine Emulator + + + + + + diff --git a/src/osd/windows/mamex64.rc b/src/osd/windows/mamex64.rc new file mode 100644 index 00000000000..76290bcd355 --- /dev/null +++ b/src/osd/windows/mamex64.rc @@ -0,0 +1,14 @@ +// +// mamex64.rc - Minimal resource file for Win32 MAME +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// + +#define NOWINRES +#include "windows.h" +#include "commctrl.h" + +#include "mamevers.rc" + +1 24 MOVEABLE PURE "mamex64.man" diff --git a/src/osd/windows/multidef.h b/src/osd/windows/multidef.h new file mode 100644 index 00000000000..1a2caed5ed0 --- /dev/null +++ b/src/osd/windows/multidef.h @@ -0,0 +1,38 @@ +//============================================================ +// +// multidef.h - Win32 Multi Monitors +// +//============================================================ + +#ifndef __MULTIDEF__ +#define __MULTIDEF__ + +/* + The MinGW implementations of windef.h and winuser.h define + a bunch of things that prevent the standard MS multimon.h + header from working as designed. This is a workaround. + + Note, we can only include multimon.h exactly once. So any + derivative builds like MAME32 should include this file + instead of multimon.h +*/ + +#if defined(SM_CMONITORS) && (WINVER < 0x0500) + +#undef MONITOR_DEFAULTTONULL +#undef MONITOR_DEFAULTTOPRIMARY +#undef MONITOR_DEFAULTTONEAREST +#undef MONITORINFOF_PRIMARY + +#define MONITOR_DEFAULTTONULL 0x00000000 +#define MONITOR_DEFAULTTOPRIMARY 0x00000001 +#define MONITOR_DEFAULTTONEAREST 0x00000002 +#define MONITORINFOF_PRIMARY 0x00000001 + +#endif /* (SM_CMONITORS) && (WINVER < 0x0500) */ + + +#define COMPILE_MULTIMON_STUBS +#include + +#endif /* __MULTIDEF__ */ diff --git a/src/osd/windows/osd_cpu.h b/src/osd/windows/osd_cpu.h new file mode 100644 index 00000000000..566217b4e47 --- /dev/null +++ b/src/osd/windows/osd_cpu.h @@ -0,0 +1,60 @@ +//============================================================ +// +// osd_cpu.h - Win32 CPU-specific data types +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +/******************************************************************************* +* * +* Define size independent data types and operations. * +* * +* The following types must be supported by all platforms: * +* * +* UINT8 - Unsigned 8-bit Integer INT8 - Signed 8-bit integer * +* UINT16 - Unsigned 16-bit Integer INT16 - Signed 16-bit integer * +* UINT32 - Unsigned 32-bit Integer INT32 - Signed 32-bit integer * +* UINT64 - Unsigned 64-bit Integer INT64 - Signed 64-bit integer * +* * +* * +* The macro names for the artithmatic operations are composed as follows: * +* * +* XXX_R_A_B, where XXX - 3 letter operation code (ADD, SUB, etc.) * +* R - The type of the result * +* A - The type of operand 1 * +* B - The type of operand 2 (if binary operation) * +* * +* Each type is one of: U8,8,U16,16,U32,32,U64,64 * +* * +*******************************************************************************/ +#pragma once + +#ifndef OSD_CPU_H +#define OSD_CPU_H + +/* Combine two 32-bit integers into a 64-bit integer */ +#define COMBINE_64_32_32(A,B) ((((UINT64)(A))<<32) | (UINT32)(B)) +#define COMBINE_U64_U32_U32(A,B) COMBINE_64_32_32(A,B) + +/* Return upper 32 bits of a 64-bit integer */ +#define HI32_32_64(A) (((UINT64)(A)) >> 32) +#define HI32_U32_U64(A) HI32_32_64(A) + +/* Return lower 32 bits of a 64-bit integer */ +#define LO32_32_64(A) ((A) & 0xffffffff) +#define LO32_U32_U64(A) LO32_32_64(A) + +#define DIV_64_64_32(A,B) ((A)/(B)) +#define DIV_U64_U64_U32(A,B) ((A)/(UINT32)(B)) + +#define MOD_32_64_32(A,B) ((A)%(B)) +#define MOD_U32_U64_U32(A,B) ((A)%(UINT32)(B)) + +#define MUL_64_32_32(A,B) ((A)*(INT64)(B)) +#define MUL_U64_U32_U32(A,B) ((A)*(UINT64)(UINT32)(B)) + + + +#endif /* defined OSD_CPU_H */ diff --git a/src/osd/windows/osinline.h b/src/osd/windows/osinline.h new file mode 100644 index 00000000000..e6131724886 --- /dev/null +++ b/src/osd/windows/osinline.h @@ -0,0 +1,562 @@ +//============================================================ +// +// osinline.h +// +// x86 inline implementations for MSVC compiler. +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __OSINLINE__ +#define __OSINLINE__ + +#ifdef PTR64 +#include +#pragma intrinsic(_BitScanReverse) +#else +#pragma intrinsic(_InterlockedCompareExchange) +#pragma intrinsic(_InterlockedExchange) +#pragma intrinsic(_InterlockedExchangeAdd) +#endif + + +/*************************************************************************** + INLINE MATH FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + mul_32x32 - perform a signed 32 bit x 32 bit + multiply and return the full 64 bit result +-------------------------------------------------*/ + +#ifndef PTR64 +#define mul_32x32 _mul_32x32 +INLINE INT64 _mul_32x32(INT32 a, INT32 b) +{ + // in theory this should work, but it is untested + __asm + { + mov eax,a + imul b + // leave results in edx:eax + } +} +#endif + + +/*------------------------------------------------- + mulu_32x32 - perform an unsigned 32 bit x + 32 bit multiply and return the full 64 bit + result +-------------------------------------------------*/ + +#ifndef PTR64 +#define mulu_32x32 _mulu_32x32 +INLINE UINT64 _mulu_32x32(UINT32 a, UINT32 b) +{ + // in theory this should work, but it is untested + __asm + { + mov eax,a + mul b + // leave results in edx:eax + } +} +#endif + + +/*------------------------------------------------- + mul_32x32_hi - perform a signed 32 bit x 32 bit + multiply and return the upper 32 bits of the + result +-------------------------------------------------*/ + +#ifndef PTR64 +#define mul_32x32_hi _mul_32x32_hi +INLINE INT32 _mul_32x32_hi(INT32 a, INT32 b) +{ + INT32 result; + + __asm + { + mov eax,a + imul b + mov result,edx + } + + return result; +} +#endif + + +/*------------------------------------------------- + mulu_32x32_hi - perform an unsigned 32 bit x + 32 bit multiply and return the upper 32 bits + of the result +-------------------------------------------------*/ + +#ifndef PTR64 +#define mulu_32x32_hi _mulu_32x32_hi +INLINE UINT32 _mulu_32x32_hi(UINT32 a, UINT32 b) +{ + INT32 result; + + __asm + { + mov eax,a + mul b + mov result,edx + } + + return result; +} +#endif + + +/*------------------------------------------------- + mul_32x32_shift - perform a signed 32 bit x + 32 bit multiply and shift the result by the + given number of bits before truncating the + result to 32 bits +-------------------------------------------------*/ + +#ifndef PTR64 +#define mul_32x32_shift _mul_32x32_shift +INLINE INT32 _mul_32x32_shift(INT32 a, INT32 b, UINT8 shift) +{ + INT32 result; + + __asm + { + mov eax,a + imul b + mov cl,shift + shrd eax,edx,cl + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + mulu_32x32_shift - perform an unsigned 32 bit x + 32 bit multiply and shift the result by the + given number of bits before truncating the + result to 32 bits +-------------------------------------------------*/ + +#ifndef PTR64 +#define mulu_32x32_shift _mulu_32x32_shift +INLINE UINT32 _mulu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift) +{ + INT32 result; + + __asm + { + mov eax,a + mul b + mov cl,shift + shrd eax,edx,cl + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + div_64x32 - perform a signed 64 bit x 32 bit + divide and return the 32 bit quotient +-------------------------------------------------*/ + +#ifndef PTR64 +#define div_64x32 _div_64x32 +INLINE INT32 _div_64x32(INT64 a, INT32 b) +{ + INT32 result; + INT32 alow = a; + INT32 ahigh = a >> 32; + + __asm + { + mov eax,alow + mov edx,ahigh + idiv b + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + divu_64x32 - perform an unsigned 64 bit x 32 bit + divide and return the 32 bit quotient +-------------------------------------------------*/ + +#ifndef PTR64 +#define divu_64x32 _divu_64x32 +INLINE UINT32 _divu_64x32(UINT64 a, UINT32 b) +{ + UINT32 result; + UINT32 alow = a; + UINT32 ahigh = a >> 32; + + __asm + { + mov eax,alow + mov edx,ahigh + div b + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + div_64x32_rem - perform a signed 64 bit x 32 + bit divide and return the 32 bit quotient and + 32 bit remainder +-------------------------------------------------*/ + +#ifndef PTR64 +#define div_64x32_rem _div_64x32_rem +INLINE INT32 _div_64x32_rem(INT64 a, INT32 b, INT32 *remainder) +{ + INT32 result; + INT32 alow = a; + INT32 ahigh = a >> 32; + INT32 rem; + + __asm + { + mov eax,alow + mov edx,ahigh + idiv b + mov result,eax + mov rem,edx + } + + *remainder = rem; + return result; +} +#endif + + +/*------------------------------------------------- + divu_64x32_rem - perform an unsigned 64 bit x + 32 bit divide and return the 32 bit quotient + and 32 bit remainder +-------------------------------------------------*/ + +#ifndef PTR64 +#define divu_64x32_rem _divu_64x32_rem +INLINE UINT32 _divu_64x32_rem(UINT64 a, UINT32 b, UINT32 *remainder) +{ + UINT32 result; + UINT32 alow = a; + UINT32 ahigh = a >> 32; + UINT32 rem; + + __asm + { + mov eax,alow + mov edx,ahigh + div b + mov result,eax + mov rem,edx + } + + *remainder = rem; + return result; +} +#endif + + +/*------------------------------------------------- + div_32x32_shift - perform a signed divide of + two 32 bit values, shifting the first before + division, and returning the 32 bit quotient +-------------------------------------------------*/ + +#ifndef PTR64 +#define div_32x32_shift _div_32x32_shift +INLINE INT32 _div_32x32_shift(INT32 a, INT32 b, UINT8 shift) +{ + INT32 result; + + __asm + { + mov eax,a + cdq + mov cl,shift + shld edx,eax,cl + shl eax,cl + idiv b + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + divu_32x32_shift - perform an unsigned divide of + two 32 bit values, shifting the first before + division, and returning the 32 bit quotient +-------------------------------------------------*/ + +#ifndef PTR64 +#define divu_32x32_shift _divu_32x32_shift +INLINE UINT32 _divu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift) +{ + UINT32 result; + + __asm + { + mov eax,a + xor edx,edx + mov cl,shift + shld edx,eax,cl + shl eax,cl + div b + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + mod_64x32 - perform a signed 64 bit x 32 bit + divide and return the 32 bit remainder +-------------------------------------------------*/ + +#ifndef PTR64 +#define mod_64x32 _mod_64x32 +INLINE INT32 _mod_64x32(INT64 a, INT32 b) +{ + INT32 result; + INT32 alow = a; + INT32 ahigh = a >> 32; + + __asm + { + mov eax,alow + mov edx,ahigh + idiv b + mov result,edx + } + + return result; +} +#endif + + +/*------------------------------------------------- + modu_64x32 - perform an unsigned 64 bit x 32 bit + divide and return the 32 bit remainder +-------------------------------------------------*/ + +#ifndef PTR64 +#define modu_64x32 _modu_64x32 +INLINE UINT32 _modu_64x32(UINT64 a, UINT32 b) +{ + UINT32 result; + UINT32 alow = a; + UINT32 ahigh = a >> 32; + + __asm + { + mov eax,alow + mov edx,ahigh + div b + mov result,edx + } + + return result; +} +#endif + + +/*------------------------------------------------- + recip_approx - compute an approximate floating + point reciprocal +-------------------------------------------------*/ + +#ifdef PTR64 +#define recip_approx _recip_approx +INLINE float _recip_approx(float z) +{ + __m128 mz = _mm_set_ss(z); + __m128 mooz = _mm_rcp_ss(mz); + float ooz; + _mm_store_ss(&ooz, mooz); + return ooz; +} +#endif + + + +/*************************************************************************** + INLINE BIT MANIPULATION FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + count_leading_zeros - return the number of + leading zero bits in a 32-bit value +-------------------------------------------------*/ + +#define count_leading_zeros _count_leading_zeros +INLINE UINT8 _count_leading_zeros(UINT32 value) +{ +#ifdef PTR64 + UINT32 index; + return _BitScanReverse(&index, value) ? (index ^ 31) : 32; +#else + INT32 result; + + __asm + { + bsr eax,value + jnz skip + mov eax,63 + skip: + xor eax,31 + mov result,eax + } + + return result; +#endif +} + + +/*------------------------------------------------- + count_leading_ones - return the number of + leading one bits in a 32-bit value +-------------------------------------------------*/ + +#define count_leading_ones _count_leading_ones +INLINE UINT8 _count_leading_ones(UINT32 value) +{ +#ifdef PTR64 + UINT32 index; + return _BitScanReverse(&index, ~value) ? (index ^ 31) : 32; +#else + INT32 result; + + __asm + { + mov eax,value + not eax + bsr eax,eax + jnz skip + mov eax,63 + skip: + xor eax,31 + mov result,eax + } + + return result; +#endif +} + + + +/*************************************************************************** + INLINE SYNCHRONIZATION FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + compare_exchange32 - compare the 'compare' + value against the memory at 'ptr'; if equal, + swap in the 'exchange' value. Regardless, + return the previous value at 'ptr'. +-------------------------------------------------*/ + +#define compare_exchange32 _compare_exchange32 +INLINE INT32 _compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange) +{ + return _InterlockedCompareExchange(ptr, exchange, compare); +} + + +/*------------------------------------------------- + compare_exchange64 - compare the 'compare' + value against the memory at 'ptr'; if equal, + swap in the 'exchange' value. Regardless, + return the previous value at 'ptr'. +-------------------------------------------------*/ + +#ifdef PTR64 +#define compare_exchange64 _compare_exchange64 +INLINE INT64 _compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange) +{ + return _InterlockedCompareExchange64(ptr, exchange, compare); +} +#endif + + +/*------------------------------------------------- + atomic_exchange32 - atomically exchange the + exchange value with the memory at 'ptr', + returning the original value. +-------------------------------------------------*/ + +#define atomic_exchange32 _atomic_exchange32 +INLINE INT32 _atomic_exchange32(INT32 volatile *ptr, INT32 exchange) +{ + return _InterlockedExchange(ptr, exchange); +} + + +/*------------------------------------------------- + atomic_add32 - atomically add the delta value + to the memory at 'ptr', returning the final + result. +-------------------------------------------------*/ + +#define atomic_add32 _atomic_add32 +INLINE INT32 _atomic_add32(INT32 volatile *ptr, INT32 delta) +{ + return _InterlockedExchangeAdd(ptr, delta) + delta; +} + + +/*------------------------------------------------- + atomic_increment32 - atomically increment the + 32-bit value in memory at 'ptr', returning the + final result. +-------------------------------------------------*/ + +#define atomic_increment32 _atomic_increment32 +INLINE INT32 _atomic_increment32(INT32 volatile *ptr) +{ + return _InterlockedIncrement(ptr); +} + + +/*------------------------------------------------- + atomic_decrement32 - atomically decrement the + 32-bit value in memory at 'ptr', returning the + final result. +-------------------------------------------------*/ + +#define atomic_decrement32 _atomic_decrement32 +INLINE INT32 _atomic_decrement32(INT32 volatile *ptr) +{ + return _InterlockedDecrement(ptr); +} + + +#endif /* __OSINLINE__ */ diff --git a/src/osd/windows/output.c b/src/osd/windows/output.c new file mode 100644 index 00000000000..c4511900ccd --- /dev/null +++ b/src/osd/windows/output.c @@ -0,0 +1,311 @@ +//============================================================ +// +// output.c - Win32 implementation of MAME output routines +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include + +// MAME headers +#include "osdepend.h" +#include "driver.h" + +// MAMEOS headers +#include "output.h" + + + +//============================================================ +// CONSTANTS +//============================================================ + +// window styles +#define WINDOW_STYLE WS_OVERLAPPEDWINDOW +#define WINDOW_STYLE_EX 0 + + + +//============================================================ +// TYPEDEFS +//============================================================ + +typedef struct _registered_client registered_client; +struct _registered_client +{ + registered_client * next; // next client in the list + LPARAM id; // client-specified ID + HWND hwnd; // client HWND +}; + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +// our HWND +static HWND output_hwnd; + +// client list +static registered_client * clientlist; + +// message IDs +static UINT om_mame_start; +static UINT om_mame_stop; +static UINT om_mame_update_state; +static UINT om_mame_register_client; +static UINT om_mame_unregister_client; +static UINT om_mame_get_id_string; + + + +//============================================================ +// FUNCTION PROTOTYPES +//============================================================ + +static void winoutput_exit(running_machine *machine); +static int create_window_class(void); +static LRESULT CALLBACK output_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam); +static LRESULT register_client(HWND hwnd, LPARAM id); +static LRESULT unregister_client(HWND hwnd, LPARAM id); +static LRESULT send_id_string(HWND hwnd, LPARAM id); +static void notifier_callback(const char *outname, INT32 value, void *param); + + + +//============================================================ +// winoutput_init +//============================================================ + +void winoutput_init(running_machine *machine) +{ + int result; + + // ensure we get cleaned up + add_exit_callback(machine, winoutput_exit); + + // reset globals + clientlist = NULL; + + // create our window class + result = create_window_class(); + assert(result == 0); + + // create a window + output_hwnd = CreateWindowEx( + WINDOW_STYLE_EX, + OUTPUT_WINDOW_CLASS, + OUTPUT_WINDOW_NAME, + WINDOW_STYLE, + 0, 0, + 1, 1, + NULL, + NULL, + GetModuleHandle(NULL), + NULL); + assert(output_hwnd != NULL); + + // allocate message ids + om_mame_start = RegisterWindowMessage(OM_MAME_START); + assert(om_mame_start != 0); + om_mame_stop = RegisterWindowMessage(OM_MAME_STOP); + assert(om_mame_stop != 0); + om_mame_update_state = RegisterWindowMessage(OM_MAME_UPDATE_STATE); + assert(om_mame_update_state != 0); + + om_mame_register_client = RegisterWindowMessage(OM_MAME_REGISTER_CLIENT); + assert(om_mame_register_client != 0); + om_mame_unregister_client = RegisterWindowMessage(OM_MAME_UNREGISTER_CLIENT); + assert(om_mame_unregister_client != 0); + om_mame_get_id_string = RegisterWindowMessage(OM_MAME_GET_ID_STRING); + assert(om_mame_get_id_string != 0); + + // broadcast a startup message + PostMessage(HWND_BROADCAST, om_mame_start, (WPARAM)output_hwnd, 0); + + // register a notifier for output changes + output_set_notifier(NULL, notifier_callback, NULL); +} + + +//============================================================ +// winoutput_exit +//============================================================ + +static void winoutput_exit(running_machine *machine) +{ + // free all the clients + while (clientlist != NULL) + { + registered_client *temp = clientlist; + clientlist = temp->next; + free(temp); + } + + // broadcast a shutdown message + PostMessage(HWND_BROADCAST, om_mame_stop, (WPARAM)output_hwnd, 0); +} + + +//============================================================ +// create_window_class +//============================================================ + +static int create_window_class(void) +{ + static UINT8 classes_created = FALSE; + + /* only do this once */ + if (!classes_created) + { + WNDCLASS wc = { 0 }; + + // initialize the description of the window class + wc.lpszClassName = OUTPUT_WINDOW_CLASS; + wc.hInstance = GetModuleHandle(NULL); + wc.lpfnWndProc = output_window_proc; + + // register the class; fail if we can't + if (!RegisterClass(&wc)) + return 1; + classes_created = TRUE; + } + + return 0; +} + + +//============================================================ +// output_window_proc +//============================================================ + +static LRESULT CALLBACK output_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + // register a new client + if (message == om_mame_register_client) + return register_client((HWND)wparam, lparam); + + // unregister a client + else if (message == om_mame_unregister_client) + return unregister_client((HWND)wparam, lparam); + + // get a string for an ID + else if (message == om_mame_get_id_string) + return send_id_string((HWND)wparam, lparam); + + else + return DefWindowProc(wnd, message, wparam, lparam); +} + + +//============================================================ +// register_client +//============================================================ + +static LRESULT register_client(HWND hwnd, LPARAM id) +{ + registered_client **client; + + // find the end of the list; if we find ourself already registered, + // return 1 + for (client = &clientlist; *client != NULL; client = &(*client)->next) + if ((*client)->id == id) + { + (*client)->hwnd = hwnd; + output_notify_all(notifier_callback, *client); + return 1; + } + + // add us to the end + *client = malloc_or_die(sizeof(**client)); + (*client)->next = NULL; + (*client)->id = id; + (*client)->hwnd = hwnd; + + // request a notification for all outputs + output_notify_all(notifier_callback, *client); + return 0; +} + + +//============================================================ +// unregister_client +//============================================================ + +static LRESULT unregister_client(HWND hwnd, LPARAM id) +{ + registered_client **client; + int found = FALSE; + + // find any matching IDs in the list and remove them + for (client = &clientlist; *client != NULL; client = &(*client)->next) + if ((*client)->id == id) + { + registered_client *temp = *client; + *client = (*client)->next; + free(temp); + found = TRUE; + } + + // return an error if not found + return found ? 0 : 1; +} + + +//============================================================ +// send_id_string +//============================================================ + +static LRESULT send_id_string(HWND hwnd, LPARAM id) +{ + copydata_id_string *temp; + COPYDATASTRUCT copydata; + const char *name; + int datalen; + + // id 0 is the name of the game + if (id == 0) + name = Machine->gamedrv->name; + else + name = output_id_to_name(id); + + // a NULL name is an empty string + if (name == NULL) + name = ""; + + // allocate memory for the message + datalen = sizeof(*temp) + strlen(name); + temp = malloc_or_die(datalen); + temp->id = id; + strcpy(temp->string, name); + + // reply by using SendMessage with WM_COPYDATA + copydata.dwData = COPYDATA_MESSAGE_ID_STRING; + copydata.cbData = datalen; + copydata.lpData = temp; + SendMessage(hwnd, WM_COPYDATA, (WPARAM)output_hwnd, (LPARAM)©data); + + // free the data + free(temp); + return 0; +} + + +//============================================================ +// send_id_string +//============================================================ + +static void notifier_callback(const char *outname, INT32 value, void *param) +{ + registered_client *client; + + // loop over clients and notify them + for (client = clientlist; client != NULL; client = client->next) + if (param == NULL || param == client) + PostMessage(client->hwnd, om_mame_update_state, output_name_to_id(outname), value); +} diff --git a/src/osd/windows/output.h b/src/osd/windows/output.h new file mode 100644 index 00000000000..e147e836aea --- /dev/null +++ b/src/osd/windows/output.h @@ -0,0 +1,97 @@ +//============================================================ +// +// output.h - Win32 implementation of MAME output routines +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __WINDOWS_OUTPUT_H__ +#define __WINDOWS_OUTPUT_H__ + + +//============================================================ +// CONSTANTS +//============================================================ + +// window parameters +#define OUTPUT_WINDOW_CLASS TEXT("MAMEOutput") +#define OUTPUT_WINDOW_NAME TEXT("MAMEOutput") + +// +// These messages are sent by MAME: +// + +// OM_MAME_START: broadcast when MAME initializes +// WPARAM = HWND of MAME's output window +// LPARAM = unused +#define OM_MAME_START TEXT("MAMEOutputStart") + +// OM_MAME_STOP: broadcast when MAME shuts down +// WPARAM = HWND of MAME's output window +// LPARAM = unused +#define OM_MAME_STOP TEXT("MAMEOutputStop") + +// OM_MAME_UPDATE_STATE: sent to registered clients when the state +// of an output changes +// WPARAM = ID of the output +// LPARAM = new value for the output +#define OM_MAME_UPDATE_STATE TEXT("MAMEOutputUpdateState") + + +// +// These messages are sent by external clients to MAME: +// + +// OM_MAME_REGISTER_CLIENT: sent to MAME to register a client +// WPARAM = HWND of client's listener window +// LPARAM = client-specified ID (must be unique) +#define OM_MAME_REGISTER_CLIENT TEXT("MAMEOutputRegister") + +// OM_MAME_UNREGISTER_CLIENT: sent to MAME to unregister a client +// WPARAM = HWND of client's listener window +// LPARAM = client-specified ID (must match registration) +#define OM_MAME_UNREGISTER_CLIENT TEXT("MAMEOutputUnregister") + +// OM_MAME_GET_ID_STRING: requests the string associated with a +// given ID. ID=0 is always the name of the game. Other IDs are +// only discoverable from a OM_MAME_UPDATE_STATE message. The +// result will be sent back as a WM_COPYDATA message with MAME's +// output window as the sender, dwData = the ID of the string, +// and lpData pointing to a NULL-terminated string. +// WPARAM = HWND of client's listener window +// LPARAM = ID you wish to know about +#define OM_MAME_GET_ID_STRING TEXT("MAMEOutputGetIDString") + + +// +// These constants are used to identify WM_COPYDATA messages +// coming from MAME: +// + +#define COPYDATA_MESSAGE_ID_STRING 1 + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef struct _copydata_id_string copydata_id_string; +struct _copydata_id_string +{ + UINT32 id; // ID that was requested + char string[1]; // string array containing the data +}; + + + +//============================================================ +// FUNCTION PROTOTYPES +//============================================================ + +void winoutput_init(running_machine *machine); + + +#endif /* __WINDOWS_OUTPUT_H__ */ diff --git a/src/osd/windows/sound.c b/src/osd/windows/sound.c new file mode 100644 index 00000000000..ca41344c69d --- /dev/null +++ b/src/osd/windows/sound.c @@ -0,0 +1,427 @@ +//============================================================ +// +// sound.c - Win32 implementation of MAME sound routines +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include + +// undef WINNT for dsound.h to prevent duplicate definition +#undef WINNT +#include + +// MAME headers +#include "osdepend.h" +#include "driver.h" +#include "osdepend.h" + +// MAMEOS headers +#include "winmain.h" +#include "window.h" +#include "video.h" +#include "config.h" +#include "sound.h" + + +//============================================================ +// DEBUGGING +//============================================================ + +#define LOG_SOUND 0 + + + +//============================================================ +// LOCAL VARIABLES +//============================================================ + +// DirectSound objects +static LPDIRECTSOUND dsound; +static DSCAPS dsound_caps; + +// sound buffers +static LPDIRECTSOUNDBUFFER primary_buffer; +static LPDIRECTSOUNDBUFFER stream_buffer; +static UINT32 stream_buffer_size; +static UINT32 stream_buffer_in; + +// descriptors and formats +static DSBUFFERDESC primary_desc; +static DSBUFFERDESC stream_desc; +static WAVEFORMATEX primary_format; +static WAVEFORMATEX stream_format; + +// buffer over/underflow counts +static int buffer_underflows; +static int buffer_overflows; + +// debugging +#if LOG_SOUND +static FILE * sound_log; +#endif + + + +//============================================================ +// PROTOTYPES +//============================================================ + +static void sound_exit(running_machine *machine); +static HRESULT dsound_init(void); +static void dsound_kill(void); +static HRESULT dsound_create_buffers(void); +static void dsound_destroy_buffers(void); + + + +//============================================================ +// winsound_init +//============================================================ + +void winsound_init(running_machine *machine) +{ +#if LOG_SOUND + sound_log = fopen("sound.log", "w"); +#endif + + // if no sound, don't create anything + if (!options_get_bool(mame_options(), OPTION_SOUND)) + return; + + // ensure we get called on the way out + add_exit_callback(machine, sound_exit); + + // attempt to initialize directsound + // don't make it fatal if we can't -- we'll just run without sound + dsound_init(); +} + + +//============================================================ +// sound_exit +//============================================================ + +static void sound_exit(running_machine *machine) +{ + // kill the buffers and dsound + dsound_destroy_buffers(); + dsound_kill(); + + // print out over/underflow stats + if (buffer_overflows || buffer_underflows) + mame_printf_verbose("Sound: buffer overflows=%d underflows=%d\n", buffer_overflows, buffer_underflows); + +#if LOG_SOUND + if (sound_log) + fprintf(sound_log, "Sound buffer: overflows=%d underflows=%d\n", buffer_overflows, buffer_underflows); + fclose(sound_log); +#endif +} + + +//============================================================ +// copy_sample_data +//============================================================ + +static void copy_sample_data(INT16 *data, int bytes_to_copy) +{ + void *buffer1, *buffer2; + DWORD length1, length2; + HRESULT result; + int cur_bytes; + + // attempt to lock the stream buffer + result = IDirectSoundBuffer_Lock(stream_buffer, stream_buffer_in, bytes_to_copy, &buffer1, &length1, &buffer2, &length2, 0); + + // if we failed, assume it was an underflow (i.e., + if (result != DS_OK) + { + buffer_underflows++; + return; + } + + // adjust the input pointer + stream_buffer_in = (stream_buffer_in + bytes_to_copy) % stream_buffer_size; + + // copy the first chunk + cur_bytes = (bytes_to_copy > length1) ? length1 : bytes_to_copy; + memcpy(buffer1, data, cur_bytes); + + // adjust for the number of bytes + bytes_to_copy -= cur_bytes; + data = (INT16 *)((UINT8 *)data + cur_bytes); + + // copy the second chunk + if (bytes_to_copy != 0) + { + cur_bytes = (bytes_to_copy > length2) ? length2 : bytes_to_copy; + memcpy(buffer2, data, cur_bytes); + } + + // unlock + result = IDirectSoundBuffer_Unlock(stream_buffer, buffer1, length1, buffer2, length2); +} + + +//============================================================ +// osd_update_audio_stream +//============================================================ + +void osd_update_audio_stream(INT16 *buffer, int samples_this_frame) +{ + int bytes_this_frame = samples_this_frame * stream_format.nBlockAlign; + DWORD play_position, write_position; + HRESULT result; + + // if no sound, there is no buffer + if (stream_buffer == NULL) + return; + + // determine the current play position + result = IDirectSoundBuffer_GetCurrentPosition(stream_buffer, &play_position, &write_position); + if (result == DS_OK) + { + DWORD stream_in; + +//DWORD orig_write = write_position; + // normalize the write position so it is always after the play position + if (write_position < play_position) + write_position += stream_buffer_size; + + // normalize the stream in position so it is always after the write position + stream_in = stream_buffer_in; + if (stream_in < write_position) + stream_in += stream_buffer_size; + + // now we should have, in order: + // <------pp---wp---si---------------> + + // if we're between play and write positions, then bump forward, but only in full chunks + while (stream_in < write_position) + { +//logerror("Underflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame); + buffer_underflows++; + stream_in += samples_this_frame; + } + + // if we're going to overlap the play position, just skip this chunk + if (stream_in + bytes_this_frame > play_position + stream_buffer_size) + { +//logerror("Overflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame); + buffer_overflows++; + return; + } + + // now we know where to copy; let's do it + stream_buffer_in = stream_in % stream_buffer_size; + copy_sample_data(buffer, bytes_this_frame); + } +} + + +//============================================================ +// osd_set_mastervolume +//============================================================ + +void osd_set_mastervolume(int attenuation) +{ + // clamp the attenuation to 0-32 range + if (attenuation > 0) + attenuation = 0; + if (attenuation < -32) + attenuation = -32; + + // set the master volume + if (stream_buffer != NULL) + IDirectSoundBuffer_SetVolume(stream_buffer, (attenuation == -32) ? DSBVOLUME_MIN : attenuation * 100); +} + + +//============================================================ +// dsound_init +//============================================================ + +static HRESULT dsound_init(void) +{ + HRESULT result; + + // create the DirectSound object + result = DirectSoundCreate(NULL, &dsound, NULL); + if (result != DS_OK) + { + mame_printf_error("Error creating DirectSound: %08x\n", (UINT32)result); + goto error; + } + + // get the capabilities + dsound_caps.dwSize = sizeof(dsound_caps); + result = IDirectSound_GetCaps(dsound, &dsound_caps); + if (result != DS_OK) + { + mame_printf_error("Error getting DirectSound capabilities: %08x\n", (UINT32)result); + goto error; + } + + // set the cooperative level + result = IDirectSound_SetCooperativeLevel(dsound, win_window_list->hwnd, DSSCL_PRIORITY); + if (result != DS_OK) + { + mame_printf_error("Error setting DirectSound cooperative level: %08x\n", (UINT32)result); + goto error; + } + + // make a format description for what we want + stream_format.wBitsPerSample = 16; + stream_format.wFormatTag = WAVE_FORMAT_PCM; + stream_format.nChannels = 2; + stream_format.nSamplesPerSec = Machine->sample_rate; + stream_format.nBlockAlign = stream_format.wBitsPerSample * stream_format.nChannels / 8; + stream_format.nAvgBytesPerSec = stream_format.nSamplesPerSec * stream_format.nBlockAlign; + + // compute the buffer size based on the output sample rate + stream_buffer_size = stream_format.nSamplesPerSec * stream_format.nBlockAlign * options_get_int(mame_options(), WINOPTION_AUDIO_LATENCY) / 10; + stream_buffer_size = (stream_buffer_size / 1024) * 1024; + if (stream_buffer_size < 1024) + stream_buffer_size = 1024; +#if LOG_SOUND + fprintf(sound_log, "stream_buffer_size = %d\n", stream_buffer_size); +#endif + + // create the buffers + result = dsound_create_buffers(); + if (result != DS_OK) + goto error; + + // start playing + result = IDirectSoundBuffer_Play(stream_buffer, 0, 0, DSBPLAY_LOOPING); + if (result != DS_OK) + { + mame_printf_error("Error playing: %08x\n", (UINT32)result); + goto error; + } + return DS_OK; + + // error handling +error: + dsound_destroy_buffers(); + dsound_kill(); + return result; +} + + +//============================================================ +// dsound_kill +//============================================================ + +static void dsound_kill(void) +{ + // release the object + if (dsound != NULL) + IDirectSound_Release(dsound); + dsound = NULL; +} + + +//============================================================ +// dsound_create_buffers +//============================================================ + +static HRESULT dsound_create_buffers(void) +{ + HRESULT result; + void *buffer; + DWORD locked; + + // create a buffer desc for the primary buffer + memset(&primary_desc, 0, sizeof(primary_desc)); + primary_desc.dwSize = sizeof(primary_desc); + primary_desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_GETCURRENTPOSITION2; + primary_desc.lpwfxFormat = NULL; + + // create the primary buffer + result = IDirectSound_CreateSoundBuffer(dsound, &primary_desc, &primary_buffer, NULL); + if (result != DS_OK) + { + mame_printf_error("Error creating primary DirectSound buffer: %08x\n", (UINT32)result); + goto error; + } + + // attempt to set the primary format + result = IDirectSoundBuffer_SetFormat(primary_buffer, &stream_format); + if (result != DS_OK) + { + mame_printf_error("Error setting primary DirectSound buffer format: %08x\n", (UINT32)result); + goto error; + } + + // get the primary format + result = IDirectSoundBuffer_GetFormat(primary_buffer, &primary_format, sizeof(primary_format), NULL); + if (result != DS_OK) + { + mame_printf_error("Error getting primary DirectSound buffer format: %08x\n", (UINT32)result); + goto error; + } + mame_printf_verbose("DirectSound: Primary buffer: %d Hz, %d bits, %d channels\n", + (int)primary_format.nSamplesPerSec, (int)primary_format.wBitsPerSample, (int)primary_format.nChannels); + + // create a buffer desc for the stream buffer + memset(&stream_desc, 0, sizeof(stream_desc)); + stream_desc.dwSize = sizeof(stream_desc); + stream_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; + stream_desc.dwBufferBytes = stream_buffer_size; + stream_desc.lpwfxFormat = &stream_format; + + // create the stream buffer + result = IDirectSound_CreateSoundBuffer(dsound, &stream_desc, &stream_buffer, NULL); + if (result != DS_OK) + { + mame_printf_error("Error creating DirectSound stream buffer: %08x\n", (UINT32)result); + goto error; + } + + // lock the buffer + result = IDirectSoundBuffer_Lock(stream_buffer, 0, stream_buffer_size, &buffer, &locked, NULL, NULL, 0); + if (result != DS_OK) + { + mame_printf_error("Error locking DirectSound stream buffer: %08x\n", (UINT32)result); + goto error; + } + + // clear the buffer and unlock it + memset(buffer, 0, locked); + IDirectSoundBuffer_Unlock(stream_buffer, buffer, locked, NULL, 0); + return DS_OK; + + // error handling +error: + dsound_destroy_buffers(); + return result; +} + + +//============================================================ +// dsound_destroy_buffers +//============================================================ + +static void dsound_destroy_buffers(void) +{ + // stop any playback + if (stream_buffer != NULL) + IDirectSoundBuffer_Stop(stream_buffer); + + // release the stream buffer + if (stream_buffer != NULL) + IDirectSoundBuffer_Release(stream_buffer); + stream_buffer = NULL; + + // release the primary buffer + if (primary_buffer != NULL) + IDirectSoundBuffer_Release(primary_buffer); + primary_buffer = NULL; +} diff --git a/src/osd/windows/sound.h b/src/osd/windows/sound.h new file mode 100644 index 00000000000..79c70c59bad --- /dev/null +++ b/src/osd/windows/sound.h @@ -0,0 +1,20 @@ +//============================================================ +// +// sound.h - Win32 implementation of MAME sound routines +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __WIN_SOUND__ +#define __WIN_SOUND__ + + +//============================================================ +// PROTOTYPES +//============================================================ + +void winsound_init(running_machine *machine); + +#endif diff --git a/src/osd/windows/strconv.c b/src/osd/windows/strconv.c new file mode 100644 index 00000000000..4cb88dbf0df --- /dev/null +++ b/src/osd/windows/strconv.c @@ -0,0 +1,108 @@ +//============================================================ +// +// strconv.h - Win32 string conversion +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include + +// MAME headers +#include "restrack.h" +#include "mame.h" + +// MAMEOS headers +#include "strconv.h" +#include "unicode.h" + + +//============================================================ +// astring_from_utf8 +//============================================================ + +CHAR *astring_from_utf8(const char *utf8string) +{ + WCHAR *wstring; + int char_count; + CHAR *result; + + // convert MAME string (UTF-8) to UTF-16 + char_count = MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, NULL, 0); + wstring = (WCHAR *)alloca(char_count * sizeof(*wstring)); + MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, wstring, char_count); + + // convert UTF-16 to "ANSI code page" string + char_count = WideCharToMultiByte(CP_ACP, 0, wstring, -1, NULL, 0, NULL, NULL); + result = (CHAR *)malloc(char_count * sizeof(*result)); + if (result != NULL) + WideCharToMultiByte(CP_ACP, 0, wstring, -1, result, char_count, NULL, NULL); + + return result; +} + + +//============================================================ +// utf8_from_astring +//============================================================ + +char *utf8_from_astring(const CHAR *astring) +{ + WCHAR *wstring; + int char_count; + CHAR *result; + + // convert "ANSI code page" string to UTF-16 + char_count = MultiByteToWideChar(CP_ACP, 0, astring, -1, NULL, 0); + wstring = (WCHAR *)alloca(char_count * sizeof(*wstring)); + MultiByteToWideChar(CP_ACP, 0, astring, -1, wstring, char_count); + + // convert UTF-16 to MAME string (UTF-8) + char_count = WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL); + result = (CHAR *)malloc(char_count * sizeof(*result)); + if (result != NULL) + WideCharToMultiByte(CP_UTF8, 0, wstring, -1, result, char_count, NULL, NULL); + + return result; +} + + +//============================================================ +// wstring_from_utf8 +//============================================================ + +WCHAR *wstring_from_utf8(const char *utf8string) +{ + int char_count; + WCHAR *result; + + // convert MAME string (UTF-8) to UTF-16 + char_count = MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, NULL, 0); + result = (WCHAR *)malloc(char_count * sizeof(*result)); + if (result != NULL) + MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, result, char_count); + + return result; +} + + +//============================================================ +// utf8_from_wstring +//============================================================ + +char *utf8_from_wstring(const WCHAR *wstring) +{ + int char_count; + char *result; + + // convert UTF-16 to MAME string (UTF-8) + char_count = WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL); + result = (char *)malloc(char_count * sizeof(*result)); + if (result != NULL) + WideCharToMultiByte(CP_UTF8, 0, wstring, -1, result, char_count, NULL, NULL); + + return result; +} diff --git a/src/osd/windows/strconv.h b/src/osd/windows/strconv.h new file mode 100644 index 00000000000..20b5a190eaa --- /dev/null +++ b/src/osd/windows/strconv.h @@ -0,0 +1,37 @@ +//============================================================ +// +// strconv.h - Win32 string conversion +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __WIN_STRCONV__ +#define __WIN_STRCONV__ + +#include "mamecore.h" + + + +//============================================================ +// FUNCTION PROTOTYPES +//============================================================ + +CHAR *astring_from_utf8(const char *s) ATTR_MALLOC; +char *utf8_from_astring(const CHAR *s) ATTR_MALLOC; + +WCHAR *wstring_from_utf8(const char *s) ATTR_MALLOC; +char *utf8_from_wstring(const WCHAR *s) ATTR_MALLOC; + +#ifdef UNICODE +#define tstring_from_utf8 wstring_from_utf8 +#define utf8_from_tstring utf8_from_wstring +#else // !UNICODE +#define tstring_from_utf8 astring_from_utf8 +#define utf8_from_tstring utf8_from_astring +#endif // UNICODE + + + +#endif // __WIN_STRCONV__ diff --git a/src/osd/windows/vconv.c b/src/osd/windows/vconv.c new file mode 100644 index 00000000000..2f034f2d369 --- /dev/null +++ b/src/osd/windows/vconv.c @@ -0,0 +1,450 @@ +//============================================================ +// +// vconv.c - VC++ parameter conversion code +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include + + + +//============================================================ +// CONSTANTS +//============================================================ + +#define PRINT_COMMAND_LINE 0 + +#define VS6 0x00060000 +#define VS7 0x00070000 +#define VS71 0x0007000a +#define VS2005 0x00080000 + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef struct _translation_info translation_info; +struct _translation_info +{ + DWORD vc_version; + const char *gcc_option; + const char *vc_option; +}; + + + +//============================================================ +// TRANSLATION TABLES +//============================================================ + +static const translation_info gcc_translate[] = +{ + { 0, "-D*", "/D*" }, + { 0, "-U*", "/U*" }, + { 0, "-I*", "/I*" }, + { 0, "-o*", "~*" }, + { 0, "-include*", "/FI*" }, + { 0, "-c", "/c~/Fo" }, + { 0, "-E", "/c~/E >" }, + { 0, "-S", "/c~/Fa" }, + { VS7, "-O0", "/Od /GS" }, + { 0, "-O0", "/Od" }, + { 0, "-O1", "/O2" }, + { 0, "-O2", "/O2" }, + { 0, "-O3", "/O2" }, + { 0, "-Os", "/O1" }, + { 0, "-g", "/Zi" }, + { VS2005, "-fno-strict-aliasing", "" }, // deprecated in VS2005 + { 0, "-fno-strict-aliasing", "/Oa" }, + { 0, "-fno-omit-frame-pointer", "" }, + { 0, "-Werror", "/WX" }, + { VS2005, "-Wall", "/Wall /W3 /wd4018 /wd4146 /wd4242 /wd4244 /wd4305 /wd4619 /wd4702 /wd4706 /wd4710 /wd4711 /wd4738 /wd4826" }, + { VS7, "-Wall", "/Wall /W3 /wd4018 /wd4146 /wd4242 /wd4244 /wd4305 /wd4550 /wd4619 /wd4702 /wd4706 /wd4710 /wd4711 /wd4826" }, + { 0, "-Wall", "/W0" }, + { VS7, "-Wno-unused", "/wd4100 /wd4101 /wd4102" }, + { 0, "-W*", "" }, + { VS2005, "-march=*", "" }, // deprecated in VS2005 + { 0, "-march=pentium", "/G5" }, + { 0, "-march=athlon", "/G7" }, + { 0, "-march=pentiumpro", "/G6" }, + { 0, "-march=pentium4", "/G7" }, + { 0, "-march=athlon64", "/G7" }, + { 0, "-march=pentium3", "/G6" }, + { VS71, "-msse2", "/arch:SSE2" }, + { 0, "-msse2", "" }, + { 0, "-mwindows", "" }, + { 0, "-mno-cygwin", "" }, + { 0, "-std=gnu89", "" }, + { 0 } +}; + +static const translation_info ld_translate[] = +{ + { 0, "-l*", "*.lib" }, + { 0, "-o*", "/out:*" }, + { 0, "-Wl,-Map,*", "/map:*" }, + { 0, "-Wl,--allow-multiple-definition", "/force:multiple" }, + { 0, "-Wl,--warn-common", "" }, + { 0, "-mno-cygwin", "" }, + { 0, "-s", "" }, + { 0, "-WO", "" }, + { 0, "-mconsole", "/subsystem:console" }, + { 0, "-mwindows", "/subsystem:windows" }, + { 0, "-shared", "/dll" }, + { 0 } +}; + +static const translation_info ar_translate[] = +{ + { 0, "-cr", "" }, + { 0 } +}; + + +static const translation_info windres_translate[] = +{ + { 0, "-D*", "/D*" }, + { 0, "-U*", "/U*" }, + { 0, "-I*", "/I*" }, + { 0, "--include-dir*", "/I*" }, + { 0, "-o*", "/fo*" }, + { 0, "-O*", "" }, + { 0, "-i*", "*" }, + { 0 } +}; + + + +//============================================================ +// GLOBALS +//============================================================ + +static char command_line[32768]; + + + +//============================================================ +// get_exe_version +//============================================================ + +static DWORD get_exe_version(const char *executable) +{ + char path[MAX_PATH]; + void *version_info; + DWORD version_info_size, dummy; + void *sub_buffer; + UINT sub_buffer_size; + VS_FIXEDFILEINFO *info; + DWORD product_version; + char sub_block[2] = { '\\', '\0' }; + + // try to locate the executable + if (!SearchPath(NULL, executable, NULL, sizeof(path) / sizeof(path[0]), path, NULL)) + { + fprintf(stderr, "Cannot find %s\n", executable); + exit(-100); + } + + // determine the size of the version info + version_info_size = GetFileVersionInfoSize(path, &dummy); + if (version_info_size == 0) + { + switch(GetLastError()) + { + case ERROR_RESOURCE_TYPE_NOT_FOUND: + fprintf(stderr, "\"%s\" does not contain version info; this is probably not a MSVC executable\n", path); + break; + + default: + fprintf(stderr, "GetFileVersionInfoSize() failed\n"); + break; + } + exit(-100); + } + + // allocate the memory; using GlobalAlloc() so that we do not + // unintentionally uses malloc() overrides + version_info = GlobalAlloc(GMEM_FIXED, version_info_size); + if (!version_info) + { + fprintf(stderr, "Out of memory\n"); + exit(-100); + } + + // retrieve the version info + if (!GetFileVersionInfo(path, 0, version_info_size, version_info)) + { + fprintf(stderr, "GetFileVersionInfo() failed\n"); + exit(-100); + } + + // extract the VS_FIXEDFILEINFO from the version info + if (!VerQueryValue(version_info, sub_block, &sub_buffer, &sub_buffer_size)) + { + fprintf(stderr, "VerQueryValue() failed\n"); + exit(-100); + } + + info = (VS_FIXEDFILEINFO *) sub_buffer; + product_version = info->dwProductVersionMS; + + GlobalFree(version_info); + return product_version; +} + + + +//============================================================ +// build_command_line +//============================================================ + +static void build_command_line(int argc, char *argv[]) +{ + const translation_info *transtable; + const char *executable; + const char *outstring = ""; + char *dst = command_line; + int output_is_first = 0; + int param; + DWORD exe_version; + + // if no parameters, show usage + if (argc < 2) + { + fprintf(stderr, "Usage:\n vconv {gcc|ar|ld} [param [...]]\n"); + exit(0); + } + + // first parameter determines the type + if (!strcmp(argv[1], "gcc")) + { + transtable = gcc_translate; + executable = "cl.exe"; + dst += sprintf(dst, "cl /nologo "); + } + else if (!strcmp(argv[1], "windres")) + { + transtable = windres_translate; + executable = "rc.exe"; + dst += sprintf(dst, "rc "); + } + else if (!strcmp(argv[1], "ld")) + { + transtable = ld_translate; + executable = "link.exe"; + dst += sprintf(dst, "link /nologo /debug "); + } + else if (!strcmp(argv[1], "ar")) + { + transtable = ar_translate; + executable = "lib.exe"; + dst += sprintf(dst, "link /lib /nologo "); + outstring = "/out:"; + output_is_first = 1; + } + else + { + fprintf(stderr, "Error: unknown translation type '%s'\n", argv[1]); + exit(-100); + } + + // identify the version number of the EXE + exe_version = get_exe_version(executable); + + // special case + if (!strcmp(executable, "cl.exe") && (exe_version >= 0x00070000)) + dst += sprintf(dst, "/wd4025 "); + + // iterate over parameters + for (param = 2; param < argc; param++) + { + const char *src = argv[param]; + int srclen = strlen(src); + int i; + + // find a match + if (src[0] == '-') + { + for (i = 0; transtable[i].gcc_option; i++) + { + const char *compare = transtable[i].gcc_option; + const char *replace; + int j; + + // check version number + if (exe_version < transtable[i].vc_version) + continue; + + // find a match + for (j = 0; j < srclen; j++) + if (src[j] != compare[j]) + break; + + // if we hit an asterisk, we're ok + if (compare[j] == '*') + { + // if this is the end of the parameter, use the next one + if (src[j] == 0) + src = argv[++param]; + else + src += j; + + // copy the replacement up to the asterisk + replace = transtable[i].vc_option; + while (*replace && *replace != '*') + { + if (*replace == '~') + { + dst += sprintf(dst, "%s", outstring); + replace++; + } + else + *dst++ = *replace++; + } + + // if we have an asterisk in the replacement, copy the rest of the source + if (*replace == '*') + { + int addquote = (strchr(src, ' ') != NULL); + + if (addquote) + *dst++ = '"'; + while (*src) + { + *dst++ = (*src == '/') ? '\\' : *src; + src++; + } + if (addquote) + *dst++ = '"'; + + // if there's stuff after the asterisk, copy that + replace++; + while (*replace) + *dst++ = *replace++; + } + + // append a final space + *dst++ = ' '; + break; + } + + // if we hit the end, we're also ok + else if (compare[j] == 0 && j == srclen) + { + // copy the replacement up to the tilde + replace = transtable[i].vc_option; + while (*replace && *replace != '~') + *dst++ = *replace++; + + // if we hit a tilde, set the new output + if (*replace == '~') + outstring = replace + 1; + + // append a final space + *dst++ = ' '; + break; + } + + // else keep looking + } + + // warn if we didn't get a match + if (!transtable[i].gcc_option) + fprintf(stderr, "Unable to match parameter '%s'\n", src); + } + + // otherwise, assume it's a filename and copy translating slashes + // it can also be a Windows-specific option which is passed through unscathed + else + { + int dotrans = (*src != '/'); + + // if the output filename is implicitly first, append the out parameter + if (output_is_first) + { + dst += sprintf(dst, "%s", outstring); + output_is_first = 0; + } + + // now copy the rest of the string + while (*src) + { + *dst++ = (dotrans && *src == '/') ? '\\' : *src; + src++; + } + *dst++ = ' '; + } + } + + // trim remaining spaces and NULL terminate + while (dst > command_line && dst[-1] == ' ') + dst--; + *dst = 0; +} + + + +//============================================================ +// main +//============================================================ + +int main(int argc, char *argv[]) +{ + PROCESS_INFORMATION pi; + STARTUPINFO si; + DWORD exitcode; + int uses_redirection, in_quotes, i; + static const char cmd_prefix[] = "cmd.exe /c "; + + // build the new command line + build_command_line(argc, argv); + + // do we use redirection? if so, use cmd.exe + uses_redirection = FALSE; + in_quotes = FALSE; + for (i = 0; command_line[i]; i++) + { + if (command_line[i] == '\"') + in_quotes = !in_quotes; + if (!in_quotes && strchr("|<>", command_line[i])) + uses_redirection = TRUE; + } + if (uses_redirection) + { + memmove(command_line + strlen(cmd_prefix), command_line, strlen(command_line) + 1); + memcpy(command_line, cmd_prefix, strlen(cmd_prefix)); + } + + if (PRINT_COMMAND_LINE) + printf("%s\n", command_line); + + // create the process information structures + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + memset(&pi, 0, sizeof(pi)); + + // create and execute the process + if (!CreateProcess(NULL, command_line, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) || + pi.hProcess == INVALID_HANDLE_VALUE) + return -101; + + // block until done and fetch the error code + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, &exitcode); + + // clean up the handles + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + // return the child's error code + return exitcode; +} diff --git a/src/osd/windows/verinfo.c b/src/osd/windows/verinfo.c new file mode 100644 index 00000000000..c43a742bd5d --- /dev/null +++ b/src/osd/windows/verinfo.c @@ -0,0 +1,267 @@ +//============================================================ +// +// verinfo.c - Version resource emitter code +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#include +#include + +#include "osdcore.h" + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef struct _version_info version_info; +struct _version_info +{ + int version_major; + int version_minor; + int version_build; + int version_subbuild; + const char *version_string; + const char *author; + const char *comments; + const char *company_name; + const char *file_description; + const char *internal_name; + const char *legal_copyright; + const char *original_filename; + const char *product_name; +}; + + + +//============================================================ +// emit_version_info +//============================================================ + +static void emit_version_info(const version_info *v) +{ + printf("VS_VERSION_INFO VERSIONINFO\n"); + printf("\tFILEVERSION %d,%d,%d,%d\n", v->version_major, v->version_minor, v->version_build, v->version_subbuild); + printf("\tPRODUCTVERSION %d,%d,%d,%d\n", v->version_major, v->version_minor, v->version_build, v->version_subbuild); + printf("\tFILEFLAGSMASK 0x3fL\n"); +#ifdef MAME_DEBUG + if (v->version_build == 0) + printf("\tFILEFLAGS VS_FF_DEBUG\n"); + else + printf("\tFILEFLAGS VS_FF_PRERELEASE | VS_FF_DEBUG\n"); +#else + if (v->version_build == 0) + printf("\tFILEFLAGS 0x0L\n"); + else + printf("\tFILEFLAGS VS_FF_PRERELEASE\n"); +#endif + printf("\tFILEOS VOS_NT_WINDOWS32\n"); + printf("\tFILETYPE VFT_APP\n"); + printf("\tFILESUBTYPE VFT2_UNKNOWN\n"); + printf("BEGIN\n"); + printf("\tBLOCK \"StringFileInfo\"\n"); + printf("\tBEGIN\n"); + printf("#ifdef UNICODE\n"); + printf("\t\tBLOCK \"040904b0\"\n"); + printf("#else\n"); + printf("\t\tBLOCK \"040904E4\"\n"); + printf("#endif\n"); + printf("\t\tBEGIN\n"); + if (v->author != NULL) + printf("\t\t\tVALUE \"Author\", \"%s\\0\"\n", v->author); + if (v->comments != NULL) + printf("\t\t\tVALUE \"Comments\", \"%s\\0\"\n", v->comments); + if (v->company_name != NULL) + printf("\t\t\tVALUE \"CompanyName\", \"%s\\0\"\n", v->company_name); + if (v->file_description != NULL) + printf("\t\t\tVALUE \"FileDescription\", \"%s\\0\"\n", v->file_description); + printf("\t\t\tVALUE \"FileVersion\", \"%d, %d, %d, %d\\0\"\n", v->version_major, v->version_minor, v->version_build, v->version_subbuild); + if (v->internal_name != NULL) + printf("\t\t\tVALUE \"InternalName\", \"%s\\0\"\n", v->internal_name); + if (v->legal_copyright != NULL) + printf("\t\t\tVALUE \"LegalCopyright\", \"%s\\0\"\n", v->legal_copyright); + if (v->original_filename != NULL) + printf("\t\t\tVALUE \"OriginalFilename\", \"%s\\0\"\n", v->original_filename); + if (v->product_name != NULL) + printf("\t\t\tVALUE \"ProductName\", \"%s\\0\"\n", v->product_name); + printf("\t\t\tVALUE \"ProductVersion\", \"%s\\0\"\n", v->version_string); + printf("\t\tEND\n"); + printf("\tEND\n"); + printf("\tBLOCK \"VarFileInfo\"\n"); + printf("\tBEGIN\n"); + printf("#ifdef UNICODE\n"); + printf("\t\tVALUE \"Translation\", 0x409, 1200\n"); + printf("#else\n"); + printf("\t\tVALUE \"Translation\", 0x409, 1252\n"); + printf("#endif\n"); + printf("\tEND\n"); + printf("END\n"); +} + + + +//============================================================ +// parse_version_digit +//============================================================ + +static int parse_version_digit(const char *str, int *position) +{ + int value = 0; + + while (str[*position] != 0 && !isspace(str[*position]) && !isdigit(str[*position])) + (*position)++; + + if (str[*position] != 0 && isdigit(str[*position])) + { + sscanf(&str[*position], "%d", &value); + while (isdigit(str[*position])) + (*position)++; + } + return value; +} + + + +//============================================================ +// parse_version +//============================================================ + +static int parse_version(char *str, int *version_major, int *version_minor, int *version_micro, int *year, const char **version_string) +{ + char *copyright; + char *version; + int position = 0; + + // find the version string + version = strstr(str, "build_version"); + if (version != NULL) + version = strchr(version, '"'); + if (version == NULL) + { + fprintf(stderr, "Unable to find build_version string\n"); + return 1; + } + version++; + *strchr(version, ' ') = 0; + + *version_string = version; + *version_major = parse_version_digit(version, &position); + *version_minor = parse_version_digit(version, &position); + *version_micro = parse_version_digit(version, &position); + + // find the copyright string + copyright = strstr(str, "(c)"); + if (copyright != NULL) + copyright = strchr(copyright, '-'); + if (copyright == NULL) + { + fprintf(stderr, "Unable to find copyright string\n"); + return 1; + } + copyright++; + + *year = 0; + sscanf(copyright, "%d", year); + return 0; +} + + + +//============================================================ +// main +//============================================================ + +int main(int argc, char *argv[]) +{ + version_info v; + int begin_year, current_year; + char legal_copyright[512]; + char *buffer; + size_t size; + FILE *f; + + memset(&v, 0, sizeof(v)); + + // validate parameters + if (argc < 2) + { + printf("Usage: %s \n", argv[0]); + return 0; + } + + // open the file + f = fopen(argv[1], "rb"); + if (f == NULL) + { + fprintf(stderr, "Error opening file %s\n", argv[1]); + return 1; + } + + // get the file size + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + + // allocate a buffer + buffer = malloc(size + 1); + if (buffer == NULL) + { + fclose(f); + fprintf(stderr, "Error allocating %d bytes\n", size + 1); + return 1; + } + + // read the file contents and NULL-terminate + fread(buffer, 1, size, f); + fclose(f); + buffer[size] = 0; + + // parse out version string + if (parse_version(buffer, &v.version_major, &v.version_minor, &v.version_build, ¤t_year, &v.version_string)) + return 1; + +#ifdef MESS + // MESS + v.author = "MESS Team"; + v.comments = "Multi Emulation Super System"; + v.company_name = "MESS Team"; + v.file_description = "Multi Emulation Super System"; + v.internal_name = "MESS"; + v.original_filename = "MESS"; + v.product_name = "MESS"; + begin_year = 1998; +#elif defined(WINUI) + // MAMEUI + v.author = "Christopher Kirmse and the MAMEUI team"; + v.comments = "Multiple Arcade Machine Emulator with GUI"; + v.company_name = "MAME Team"; + v.file_description = "Multiple Arcade Machine Emulator with GUI"; + v.internal_name = "MAMEUI"; + v.original_filename = "MAMEUI"; + v.product_name = "MAMEUI"; + begin_year = 1997; +#else + // MAME + v.author = "Nicola Salmoria and the MAME Team"; + v.comments = "Multiple Arcade Machine Emulator"; + v.company_name = "MAME Team"; + v.file_description = "Multiple Arcade Machine Emulator"; + v.internal_name = "MAME"; + v.original_filename = "MAME"; + v.product_name = "MAME"; + begin_year = 1996; +#endif + + // build legal_copyright string + v.legal_copyright = legal_copyright; + snprintf(legal_copyright, ARRAY_LENGTH(legal_copyright), "Copyright %d-%d Nicola Salmoria and the MAME team", begin_year, current_year); + + // emit the info + emit_version_info(&v); + + free(buffer); + return 0; +} diff --git a/src/osd/windows/video.c b/src/osd/windows/video.c new file mode 100644 index 00000000000..694b9b5a345 --- /dev/null +++ b/src/osd/windows/video.c @@ -0,0 +1,524 @@ +//============================================================ +// +// video.c - Win32 video handling +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// needed for multimonitor +#define _WIN32_WINNT 0x501 + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include + +// standard C headers +#include + +// Windows 95/NT4 multimonitor stubs +#ifdef WIN95_MULTIMON +#include "multidef.h" +#endif + +// MAME headers +#include "osdepend.h" +#include "driver.h" +#include "profiler.h" +#include "video/vector.h" +#include "render.h" +#include "rendutil.h" +#include "ui.h" + +// MAMEOS headers +#include "winmain.h" +#include "video.h" +#include "window.h" +#include "input.h" +#include "debugwin.h" +#include "strconv.h" +#include "config.h" + +#ifdef MESS +#include "menu.h" +#endif + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +win_video_config video_config; + + + +//============================================================ +// LOCAL VARIABLES +//============================================================ + +// monitor info +win_monitor_info *win_monitor_list; +static win_monitor_info *primary_monitor; + +static mame_bitmap *effect_bitmap; + + + +//============================================================ +// PROTOTYPES +//============================================================ + +static void video_exit(running_machine *machine); +static void init_monitors(void); +static BOOL CALLBACK monitor_enum_callback(HMONITOR handle, HDC dc, LPRECT rect, LPARAM data); +static win_monitor_info *pick_monitor(int index); + +static void check_osd_inputs(void); + +static void extract_video_config(void); +static void load_effect_overlay(const char *filename); +static float get_aspect(const char *name, int report_error); +static void get_resolution(const char *name, win_window_config *config, int report_error); + + + +//============================================================ +// winvideo_init +//============================================================ + +void winvideo_init(running_machine *machine) +{ + int index; + + // ensure we get called on the way out + add_exit_callback(machine, video_exit); + + // extract data from the options + extract_video_config(); + + // set up monitors first + init_monitors(); + + // initialize the window system so we can make windows + winwindow_init(machine); + + // create the windows + for (index = 0; index < video_config.numscreens; index++) + winwindow_video_window_create(index, pick_monitor(index), &video_config.window[index]); + if (video_config.mode != VIDEO_MODE_NONE) + SetForegroundWindow(win_window_list->hwnd); + + // possibly create the debug window, but don't show it yet +#ifdef MAME_DEBUG + if (options_get_bool(mame_options(), OPTION_DEBUG)) + debugwin_init_windows(); +#endif +} + + +//============================================================ +// video_exit +//============================================================ + +static void video_exit(running_machine *machine) +{ + // free the overlay effect + if (effect_bitmap != NULL) + bitmap_free(effect_bitmap); + effect_bitmap = NULL; + + // possibly kill the debug window +#ifdef MAME_DEBUG + if (options_get_bool(mame_options(), OPTION_DEBUG)) + debugwin_destroy_windows(); +#endif + + // free all of our monitor information + while (win_monitor_list != NULL) + { + win_monitor_info *temp = win_monitor_list; + win_monitor_list = temp->next; + free(temp); + } +} + + + +//============================================================ +// winvideo_monitor_refresh +//============================================================ + +void winvideo_monitor_refresh(win_monitor_info *monitor) +{ + HRESULT result; + + // fetch the latest info about the monitor + monitor->info.cbSize = sizeof(monitor->info); + result = GetMonitorInfo(monitor->handle, (LPMONITORINFO)&monitor->info); + assert(result); +} + + + +//============================================================ +// winvideo_monitor_get_aspect +//============================================================ + +float winvideo_monitor_get_aspect(win_monitor_info *monitor) +{ + // refresh the monitor information and compute the aspect + if (video_config.keepaspect) + { + int width, height; + winvideo_monitor_refresh(monitor); + width = rect_width(&monitor->info.rcMonitor); + height = rect_height(&monitor->info.rcMonitor); + return monitor->aspect / ((float)width / (float)height); + } + return 0.0f; +} + + + +//============================================================ +// winvideo_monitor_from_handle +//============================================================ + +win_monitor_info *winvideo_monitor_from_handle(HMONITOR hmonitor) +{ + win_monitor_info *monitor; + + // find the matching monitor + for (monitor = win_monitor_list; monitor != NULL; monitor = monitor->next) + if (monitor->handle == hmonitor) + return monitor; + return NULL; +} + + + +//============================================================ +// osd_update +//============================================================ + +void osd_update(int skip_redraw) +{ + win_window_info *window; + + // if we're not skipping this redraw, update all windows + if (!skip_redraw) + for (window = win_window_list; window != NULL; window = window->next) + winwindow_video_window_update(window); + + // poll the joystick values here + winwindow_process_events(TRUE); + wininput_poll(); + check_osd_inputs(); +} + + + +//============================================================ +// init_monitors +//============================================================ + +static void init_monitors(void) +{ + win_monitor_info **tailptr; + + // make a list of monitors + win_monitor_list = NULL; + tailptr = &win_monitor_list; + EnumDisplayMonitors(NULL, NULL, monitor_enum_callback, (LPARAM)&tailptr); + + // if we're verbose, print the list of monitors + { + win_monitor_info *monitor; + for (monitor = win_monitor_list; monitor != NULL; monitor = monitor->next) + { + char *utf8_device = utf8_from_tstring(monitor->info.szDevice); + if (utf8_device != NULL) + { + mame_printf_verbose("Video: Monitor %p = \"%s\" %s\n", monitor->handle, utf8_device, (monitor == primary_monitor) ? "(primary)" : ""); + free(utf8_device); + } + } + } +} + + + +//============================================================ +// monitor_enum_callback +//============================================================ + +static BOOL CALLBACK monitor_enum_callback(HMONITOR handle, HDC dc, LPRECT rect, LPARAM data) +{ + win_monitor_info ***tailptr = (win_monitor_info ***)data; + win_monitor_info *monitor; + MONITORINFOEX info; + BOOL result; + + // get the monitor info + info.cbSize = sizeof(info); + result = GetMonitorInfo(handle, (LPMONITORINFO)&info); + assert(result); + + // allocate a new monitor info + monitor = malloc_or_die(sizeof(*monitor)); + memset(monitor, 0, sizeof(*monitor)); + + // copy in the data + monitor->handle = handle; + monitor->info = info; + + // guess the aspect ratio assuming square pixels + monitor->aspect = (float)(info.rcMonitor.right - info.rcMonitor.left) / (float)(info.rcMonitor.bottom - info.rcMonitor.top); + + // save the primary monitor handle + if (monitor->info.dwFlags & MONITORINFOF_PRIMARY) + primary_monitor = monitor; + + // hook us into the list + **tailptr = monitor; + *tailptr = &monitor->next; + + // enumerate all the available monitors so to list their names in verbose mode + return TRUE; +} + + + +//============================================================ +// pick_monitor +//============================================================ + +static win_monitor_info *pick_monitor(int index) +{ + const char *scrname, *scrname2; + win_monitor_info *monitor; + int moncount = 0; + char option[20]; + float aspect; + + // get the screen option + scrname = options_get_string(mame_options(), WINOPTION_SCREEN); + sprintf(option, "screen%d", index); + scrname2 = options_get_string(mame_options(), option); + + // decide which one we want to use + if (strcmp(scrname2, "auto") != 0) + scrname = scrname2; + + // get the aspect ratio + sprintf(option, "aspect%d", index); + aspect = get_aspect(option, TRUE); + + // look for a match in the name first + if (scrname[0] != 0) + for (monitor = win_monitor_list; monitor != NULL; monitor = monitor->next) + { + char *utf8_device; + int rc = 1; + + moncount++; + + utf8_device = utf8_from_tstring(monitor->info.szDevice); + if (utf8_device != NULL) + { + rc = strcmp(scrname, utf8_device); + free(utf8_device); + } + if (rc == 0) + goto finishit; + } + + // didn't find it; alternate monitors until we hit the jackpot + index %= moncount; + for (monitor = win_monitor_list; monitor != NULL; monitor = monitor->next) + if (index-- == 0) + goto finishit; + + // return the primary just in case all else fails + monitor = primary_monitor; + +finishit: + if (aspect != 0) + monitor->aspect = aspect; + return monitor; +} + + + +//============================================================ +// check_osd_inputs +//============================================================ + +static void check_osd_inputs(void) +{ + // check for toggling fullscreen mode + if (input_ui_pressed(IPT_OSD_1)) + winwindow_toggle_full_screen(); + +#ifdef MESS + // check for toggling menu bar + if (input_ui_pressed(IPT_OSD_2)) + win_toggle_menubar(); +#endif +} + + + +//============================================================ +// extract_video_config +//============================================================ + +static void extract_video_config(void) +{ + const char *stemp; + + // global options: extract the data + video_config.windowed = options_get_bool(mame_options(), WINOPTION_WINDOW); + video_config.prescale = options_get_int(mame_options(), WINOPTION_PRESCALE); + video_config.keepaspect = options_get_bool(mame_options(), WINOPTION_KEEPASPECT); + video_config.numscreens = options_get_int(mame_options(), WINOPTION_NUMSCREENS); +#ifdef MAME_DEBUG + // if we are in debug mode, never go full screen + if (options_get_bool(mame_options(), OPTION_DEBUG)) + video_config.windowed = TRUE; +#endif + stemp = options_get_string(mame_options(), WINOPTION_EFFECT); + if (strcmp(stemp, "none") != 0) + load_effect_overlay(stemp); + + // per-window options: extract the data + get_resolution(WINOPTION_RESOLUTION0, &video_config.window[0], TRUE); + get_resolution(WINOPTION_RESOLUTION1, &video_config.window[1], TRUE); + get_resolution(WINOPTION_RESOLUTION2, &video_config.window[2], TRUE); + get_resolution(WINOPTION_RESOLUTION3, &video_config.window[3], TRUE); + + // video options: extract the data + stemp = options_get_string(mame_options(), WINOPTION_VIDEO); + if (strcmp(stemp, "d3d") == 0) + video_config.mode = VIDEO_MODE_D3D; + else if (strcmp(stemp, "ddraw") == 0) + video_config.mode = VIDEO_MODE_DDRAW; + else if (strcmp(stemp, "gdi") == 0) + video_config.mode = VIDEO_MODE_GDI; + else if (strcmp(stemp, "none") == 0) + { + video_config.mode = VIDEO_MODE_NONE; + if (options_get_int(mame_options(), OPTION_SECONDS_TO_RUN) == 0) + mame_printf_warning("Warning: -video none doesn't make much sense without -seconds_to_run\n"); + } + else + { + mame_printf_warning("Invalid video value %s; reverting to gdi\n", stemp); + video_config.mode = VIDEO_MODE_GDI; + } + video_config.waitvsync = options_get_bool(mame_options(), WINOPTION_WAITVSYNC); + video_config.syncrefresh = options_get_bool(mame_options(), WINOPTION_SYNCREFRESH); + video_config.triplebuf = options_get_bool(mame_options(), WINOPTION_TRIPLEBUFFER); + video_config.switchres = options_get_bool(mame_options(), WINOPTION_SWITCHRES); + + // ddraw options: extract the data + video_config.hwstretch = options_get_bool(mame_options(), WINOPTION_HWSTRETCH); + + // d3d options: extract the data + video_config.filter = options_get_bool(mame_options(), WINOPTION_FILTER); + if (video_config.prescale == 0) + video_config.prescale = 1; + + // misc options: sanity check values + + // per-window options: sanity check values + + // d3d options: sanity check values + options_get_int(mame_options(), WINOPTION_D3DVERSION); + + options_get_float(mame_options(), WINOPTION_FULLSCREENBRIGHTNESS); + options_get_float(mame_options(), WINOPTION_FULLLSCREENCONTRAST); + options_get_float(mame_options(), WINOPTION_FULLSCREENGAMMA); +} + + + +//============================================================ +// load_effect_overlay +//============================================================ + +static void load_effect_overlay(const char *filename) +{ + char *tempstr = malloc_or_die(strlen(filename) + 5); + int scrnum; + char *dest; + + // append a .PNG extension + strcpy(tempstr, filename); + dest = strrchr(tempstr, '.'); + if (dest == NULL) + dest = &tempstr[strlen(tempstr)]; + strcpy(dest, ".png"); + + // load the file + effect_bitmap = render_load_png(NULL, tempstr, NULL, NULL); + if (effect_bitmap == NULL) + { + mame_printf_error("Unable to load PNG file '%s'\n", tempstr); + free(tempstr); + return; + } + + // set the overlay on all screens + for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++) + if (Machine->drv->screen[scrnum].tag != NULL) + render_container_set_overlay(render_container_get_screen(scrnum), effect_bitmap); + + free(tempstr); +} + + + +//============================================================ +// get_aspect +//============================================================ + +static float get_aspect(const char *name, int report_error) +{ + const char *defdata = options_get_string(mame_options(), WINOPTION_ASPECT); + const char *data = options_get_string(mame_options(), name); + int num = 0, den = 1; + + if (strcmp(data, "auto") == 0) + { + if (strcmp(defdata, "auto") == 0) + return 0; + data = defdata; + } + if (sscanf(data, "%d:%d", &num, &den) != 2 && report_error) + mame_printf_error("Illegal aspect ratio value for %s = %s\n", name, data); + return (float)num / (float)den; +} + + + +//============================================================ +// get_resolution +//============================================================ + +static void get_resolution(const char *name, win_window_config *config, int report_error) +{ + const char *defdata = options_get_string(mame_options(), WINOPTION_RESOLUTION); + const char *data = options_get_string(mame_options(), name); + + config->width = config->height = config->refresh = 0; + if (strcmp(data, "auto") == 0) + { + if (strcmp(defdata, "auto") == 0) + return; + data = defdata; + } + if (sscanf(data, "%dx%d@%d", &config->width, &config->height, &config->refresh) < 2 && report_error) + mame_printf_error("Illegal resolution value for %s = %s\n", name, data); +} diff --git a/src/osd/windows/video.h b/src/osd/windows/video.h new file mode 100644 index 00000000000..a3506cdc089 --- /dev/null +++ b/src/osd/windows/video.h @@ -0,0 +1,100 @@ +//============================================================ +// +// video.h - Win32 implementation of MAME video routines +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __WIN_VIDEO__ +#define __WIN_VIDEO__ + + +//============================================================ +// CONSTANTS +//============================================================ + +#define MAX_WINDOWS 4 + +#define VIDEO_MODE_NONE 0 +#define VIDEO_MODE_GDI 1 +#define VIDEO_MODE_DDRAW 2 +#define VIDEO_MODE_D3D 3 + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef struct _win_monitor_info win_monitor_info; +struct _win_monitor_info +{ + win_monitor_info * next; // pointer to next monitor in list + HMONITOR handle; // handle to the monitor + MONITORINFOEX info; // most recently retrieved info + float aspect; // computed/configured aspect ratio of the physical device + int reqwidth; // requested width for this monitor + int reqheight; // requested height for this monitor +}; + + +typedef struct _win_window_config win_window_config; +struct _win_window_config +{ + float aspect; // decoded aspect ratio + int width; // decoded width + int height; // decoded height + int refresh; // decoded refresh +}; + + +typedef struct _win_video_config win_video_config; +struct _win_video_config +{ + // global configuration + int windowed; // start windowed? + int prescale; // prescale factor + int keepaspect; // keep aspect ratio + int numscreens; // number of screens + int layerconfig; // default configuration of layers + + // per-window configuration + win_window_config window[MAX_WINDOWS]; // configuration data per-window + + // hardware options + int mode; // output mode + int waitvsync; // spin until vsync + int syncrefresh; // sync only to refresh rate + int triplebuf; // triple buffer + int switchres; // switch resolutions + + // ddraw options + int hwstretch; // stretch using the hardware + + // d3d options + int filter; // enable filtering +}; + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +extern win_monitor_info *win_monitor_list; +extern win_video_config video_config; + + +//============================================================ +// PROTOTYPES +//============================================================ + +void winvideo_init(running_machine *machine); + +void winvideo_monitor_refresh(win_monitor_info *monitor); +float winvideo_monitor_get_aspect(win_monitor_info *monitor); +win_monitor_info *winvideo_monitor_from_handle(HMONITOR monitor); + +#endif diff --git a/src/osd/windows/winalloc.c b/src/osd/windows/winalloc.c new file mode 100644 index 00000000000..a355ebca3c8 --- /dev/null +++ b/src/osd/windows/winalloc.c @@ -0,0 +1,395 @@ +//============================================================ +// +// winalloc.c - Win32 memory allocation routines +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#define _WIN32_WINNT 0x0400 + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include + +// MAME headers +#include "osdcore.h" + +// undefine any redefines we have in the prefix +#undef malloc +#undef calloc +#undef realloc + + + +//============================================================ +// CONSTANTS +//============================================================ + +#define PAGE_SIZE 4096 +#define COOKIE_VAL 0x11335577 + +// set this to 0 to not use guard pages +#define USE_GUARD_PAGES 1 + +// set this to 1 to align memory blocks to the start of a page; +// otherwise, they are aligned to the end, thus catching array +// overruns +#define ALIGN_START 0 + +// set this to 1 to record all mallocs and frees in the logfile +#define LOG_CALLS 0 + + + +//============================================================ +// TYPEDEFS +//============================================================ + +typedef struct _memory_entry memory_entry; +struct _memory_entry +{ + memory_entry * next; + memory_entry * prev; + size_t size; + void * base; + const char * file; + int line; + int id; +}; + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +int winalloc_in_main_code = FALSE; + + + +//============================================================ +// LOCAL VARIABLES +//============================================================ + +static memory_entry *alloc_list; +static memory_entry *free_list; +static int current_id; + +static CRITICAL_SECTION memory_lock; +static UINT8 memory_lock_initialized = FALSE; + + + +//============================================================ +// INLINES +//============================================================ + +INLINE void memory_lock_acquire(void) +{ + if (!memory_lock_initialized) + { + memory_lock_initialized = TRUE; + InitializeCriticalSection(&memory_lock); + } + EnterCriticalSection(&memory_lock); +} + + +INLINE void memory_lock_release(void) +{ + LeaveCriticalSection(&memory_lock); +} + + +INLINE memory_entry *allocate_entry(void) +{ + memory_entry *entry; + + memory_lock_acquire(); + + // if we're out of entries, allocate some more + if (free_list == NULL) + { + int entries_per_page = PAGE_SIZE / sizeof(memory_entry); + + // allocate a new pages' worth of entry + entry = (memory_entry *)VirtualAlloc(NULL, PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE); + if (entry == NULL) + { + fprintf(stderr, "Out of memory for malloc tracking!\n"); + exit(1); + } + + // add all the entries to the list + while (entries_per_page--) + { + entry->next = free_list; + free_list = entry; + entry++; + } + } + + // grab a free list entry + entry = free_list; + free_list = free_list->next; + + // add ourselves to the alloc list + entry->next = alloc_list; + if (entry->next) + entry->next->prev = entry; + entry->prev = NULL; + alloc_list = entry; + + memory_lock_release(); + + return entry; +} + + +INLINE memory_entry *find_entry(void *pointer) +{ + memory_entry *entry; + + // scan the list looking for a matching base + if (pointer) + { + memory_lock_acquire(); + + for (entry = alloc_list; entry; entry = entry->next) + if (entry->base == pointer) + break; + + memory_lock_release(); + return entry; + } + return NULL; +} + + +INLINE void free_entry(memory_entry *entry) +{ + memory_lock_acquire(); + + // remove ourselves from the alloc list + if (entry->prev) + entry->prev->next = entry->next; + else + alloc_list = entry->next; + if (entry->next) + entry->next->prev = entry->prev; + + // add ourself to the free list + entry->next = free_list; + free_list = entry; + + memory_lock_release(); +} + + + +//============================================================ +// IMPLEMENTATION +//============================================================ + +void *malloc_file_line(size_t size, const char *file, int line) +{ + UINT8 *page_base, *block_base; + size_t rounded_size; + memory_entry *entry; + + if (USE_GUARD_PAGES) + { + // round the size up to a page boundary + rounded_size = ((size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; + + // reserve that much memory, plus two guard pages + page_base = VirtualAlloc(NULL, rounded_size + 2 * PAGE_SIZE, MEM_RESERVE, PAGE_NOACCESS); + if (page_base == NULL) + return NULL; + + // now allow access to everything but the first and last pages + page_base = VirtualAlloc(page_base + PAGE_SIZE, rounded_size, MEM_COMMIT, PAGE_READWRITE); + if (page_base == NULL) + return NULL; + + // work backwards from the page base to get to the block base + if (ALIGN_START) + block_base = page_base; + else + block_base = page_base + rounded_size - size; + } + else + { + block_base = (UINT8 *)GlobalAlloc(GMEM_FIXED, size); + } + + // fill in the entry + entry = allocate_entry(); + entry->size = size; + entry->base = block_base; + entry->file = file; + entry->line = line; + entry->id = current_id++; + +//if (entry->size == 72 && IsDebuggerPresent()) DebugBreak(); + +#if LOG_CALLS + // logging + if (entry->file) + logerror("malloc #%06d size = %d (%s:%d)\n", entry->size, entry->file, entry->line); + else + logerror("malloc #%06d size = %d\n", entry->id, entry->size); +#endif + + return block_base; +} + + +void *CLIB_DECL malloc(size_t size) +{ + return malloc_file_line(size, NULL, 0); +} + + +void *calloc_file_line(size_t size, size_t count, const char *file, int line) +{ + // first allocate the memory + void *memory = malloc_file_line(size * count, file, line); + if (memory == NULL) + return NULL; + + // then memset it + memset(memory, 0, size * count); + return memory; +} + + +void *CLIB_DECL calloc(size_t size, size_t count) +{ + return calloc_file_line(size, count, NULL, 0); +} + + +// this function is called by beginthreadex +void *CLIB_DECL _calloc_crt(size_t size, size_t count) +{ + return calloc_file_line(size, count, NULL, 0); +} + + +void *realloc_file_line(void *memory, size_t size, const char *file, int line) +{ + void *newmemory = NULL; + + // if size is non-zero, we need to reallocate memory + if (size != 0) + { + // allocate space for the new amount + newmemory = malloc_file_line(size, file, line); + if (newmemory == NULL) + return NULL; + + // if we have an old pointer, copy it + if (memory != NULL) + { + memory_entry *entry = find_entry(memory); + if (entry == NULL) + { + if (winalloc_in_main_code) + { + fprintf(stderr, "Error: realloc a non-existant block (%s:%d)\n", file, line); + osd_break_into_debugger("Error: realloc a non-existant block\n"); + } + } + else + memcpy(newmemory, memory, (size < entry->size) ? size : entry->size); + } + } + + // if we have an original pointer, free it + if (memory != NULL) + free(memory); + + return newmemory; +} + + +void *CLIB_DECL realloc(void *memory, size_t size) +{ + return realloc_file_line(memory, size, NULL, 0); +} + + +void CLIB_DECL free(void *memory) +{ + memory_entry *entry; + + // allow NULL frees + if (memory == NULL) + return; + + // error if no entry found + entry = find_entry(memory); + if (entry == NULL) + { + if (winalloc_in_main_code) + { + fprintf(stderr, "Error: free a non-existant block\n"); + osd_break_into_debugger("Error: free a non-existant block"); + } + return; + } + free_entry(entry); + + // free the memory + if (USE_GUARD_PAGES) + VirtualFree((UINT8 *)memory - ((size_t)memory & (PAGE_SIZE-1)) - PAGE_SIZE, 0, MEM_RELEASE); + else + GlobalFree(memory); + +#if LOG_CALLS + logerror("free #%06d size = %d\n", entry->id, entry->size); +#endif +} + + +size_t CLIB_DECL _msize(void *memory) +{ + memory_entry *entry = find_entry(memory); + if (entry == NULL) + { + if (winalloc_in_main_code) + { + fprintf(stderr, "Error: msize a non-existant block\n"); + osd_break_into_debugger("Error: msize a non-existant block"); + } + return 0; + } + return entry->size; +} + + +void check_unfreed_mem(void) +{ + memory_entry *entry; + int total = 0; + + memory_lock_acquire(); + + // check for leaked memory + for (entry = alloc_list; entry; entry = entry->next) + if (entry->file != NULL) + { + if (total == 0) + fprintf(stderr, "--- memory leak warning ---\n"); + total += entry->size; + fprintf(stderr, "allocation #%06d, %d bytes (%s:%d)\n", entry->id, entry->size, entry->file, entry->line); + } + + memory_lock_release(); + + if (total > 0) + fprintf(stderr, "a total of %d bytes were not free()'d\n", total); +} diff --git a/src/osd/windows/windir.c b/src/osd/windows/windir.c new file mode 100644 index 00000000000..0bfaca04202 --- /dev/null +++ b/src/osd/windows/windir.c @@ -0,0 +1,155 @@ +/*************************************************************************** + + windir.c - Win32 OSD core directory access functions + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +// standard C headers +#include +#include + +// MAME headers +#include "osdcore.h" + +// MAMEOS headers +#include "strconv.h" +#include "winutil.h" + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +struct _osd_directory +{ + HANDLE find; // handle to the finder + int is_first; // TRUE if this is the first entry + osd_directory_entry entry; // current entry's data + WIN32_FIND_DATA data; // current raw data +}; + + + +//============================================================ +// osd_opendir +//============================================================ + +osd_directory *osd_opendir(const char *dirname) +{ + osd_directory *dir = NULL; + TCHAR *t_dirname = NULL; + TCHAR *dirfilter = NULL; + size_t dirfilter_size; + + // allocate memory to hold the osd_tool_directory structure + dir = malloc(sizeof(*dir)); + if (dir == NULL) + goto error; + memset(dir, 0, sizeof(*dir)); + + // initialize the structure + dir->find = INVALID_HANDLE_VALUE; + dir->is_first = TRUE; + + // convert the path to TCHARs + t_dirname = tstring_from_utf8(dirname); + if (t_dirname == NULL) + goto error; + + // append \*.* to the directory name + dirfilter_size = _tcslen(t_dirname) + 5; + dirfilter = (TCHAR *)malloc(dirfilter_size * sizeof(*dirfilter)); + if (dirfilter == NULL) + goto error; + _sntprintf(dirfilter, dirfilter_size, TEXT("%s\\*.*"), t_dirname); + + // attempt to find the first file + dir->find = FindFirstFile(dirfilter, &dir->data); + +error: + // cleanup + if (t_dirname != NULL) + free(t_dirname); + if (dirfilter != NULL) + free(dirfilter); + if (dir != NULL && dir->find == INVALID_HANDLE_VALUE) + { + free(dir); + dir = NULL; + } + return dir; +} + + +//============================================================ +// osd_readdir +//============================================================ + +const osd_directory_entry *osd_readdir(osd_directory *dir) +{ + // if we've previously allocated a name, free it now + if (dir->entry.name != NULL) + { + free((void *)dir->entry.name); + dir->entry.name = NULL; + } + + // if this isn't the first file, do a find next + if (!dir->is_first) + { + if (!FindNextFile(dir->find, &dir->data)) + return NULL; + } + + // otherwise, just use the data we already had + else + dir->is_first = FALSE; + + // extract the data + dir->entry.name = utf8_from_tstring(dir->data.cFileName); + dir->entry.type = win_attributes_to_entry_type(dir->data.dwFileAttributes); + dir->entry.size = dir->data.nFileSizeLow | ((UINT64) dir->data.nFileSizeHigh << 32); + return (dir->entry.name != NULL) ? &dir->entry : NULL; +} + + +//============================================================ +// osd_closedir +//============================================================ + +void osd_closedir(osd_directory *dir) +{ + // free any data associated + if (dir->entry.name != NULL) + free((void *)dir->entry.name); + if (dir->find != INVALID_HANDLE_VALUE) + FindClose(dir->find); + free(dir); +} + + +//============================================================ +// osd_is_absolute_path +//============================================================ + +int osd_is_absolute_path(const char *path) +{ + int result = FALSE; + TCHAR *t_path = tstring_from_utf8(path); + if (t_path != NULL) + { + result = !PathIsRelative(t_path); + free(t_path); + } + return result; +} + diff --git a/src/osd/windows/window.c b/src/osd/windows/window.c new file mode 100644 index 00000000000..7ccd3b1940e --- /dev/null +++ b/src/osd/windows/window.c @@ -0,0 +1,1885 @@ +//============================================================ +// +// window.c - Win32 window handling +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#define LOG_THREADS 0 +#define LOG_TEMP_PAUSE 0 + +// Needed for RAW Input +#define _WIN32_WINNT 0x501 +#define WM_INPUT 0x00FF + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +// standard C headers +#include +#include + +// MAME headers +#include "osdepend.h" +#include "driver.h" + +// MAMEOS headers +#include "winmain.h" +#include "window.h" +#include "video.h" +#include "input.h" +#include "debugwin.h" +#include "strconv.h" +#include "config.h" +#include "winutf8.h" + +#ifdef MESS +#include "menu.h" +#endif /* MESS */ + +extern int drawnone_init(win_draw_callbacks *callbacks); +extern int drawgdi_init(win_draw_callbacks *callbacks); +extern int drawdd_init(win_draw_callbacks *callbacks); +extern int drawd3d_init(win_draw_callbacks *callbacks); + + +//============================================================ +// PARAMETERS +//============================================================ + +// window styles +#define WINDOW_STYLE WS_OVERLAPPEDWINDOW +#define WINDOW_STYLE_EX 0 + +// debugger window styles +#define DEBUG_WINDOW_STYLE WS_OVERLAPPED +#define DEBUG_WINDOW_STYLE_EX 0 + +// full screen window styles +#define FULLSCREEN_STYLE WS_POPUP +#define FULLSCREEN_STYLE_EX WS_EX_TOPMOST + +// minimum window dimension +#define MIN_WINDOW_DIM 200 + +// custom window messages +#define WM_USER_FINISH_CREATE_WINDOW (WM_USER + 0) +#define WM_USER_SELF_TERMINATE (WM_USER + 1) +#define WM_USER_REDRAW (WM_USER + 2) +#define WM_USER_SET_FULLSCREEN (WM_USER + 3) +#define WM_USER_SET_MAXSIZE (WM_USER + 4) +#define WM_USER_SET_MINSIZE (WM_USER + 5) +#define WM_USER_UI_TEMP_PAUSE (WM_USER + 6) +#define WM_USER_EXEC_FUNC (WM_USER + 7) + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +win_window_info *win_window_list; +static win_window_info **last_window_ptr; +static DWORD main_threadid; + +// actual physical resolution +static int win_physical_width; +static int win_physical_height; + + + +//============================================================ +// LOCAL VARIABLES +//============================================================ + +// event handling +static DWORD last_event_check; + +// debugger +static int in_background; + +static int ui_temp_pause; +static int ui_temp_was_paused; + +static int multithreading_enabled; + +static HANDLE window_thread; +static DWORD window_threadid; + +static DWORD last_update_time; + +static win_draw_callbacks draw; + +static HANDLE ui_pause_event; +static HANDLE window_thread_ready_event; + + + +//============================================================ +// PROTOTYPES +//============================================================ + +static void winwindow_exit(running_machine *machine); +static void winwindow_video_window_destroy(win_window_info *window); +static void draw_video_contents(win_window_info *window, HDC dc, int update); + +static unsigned __stdcall thread_entry(void *param); +static int complete_create(win_window_info *window); +static void create_window_class(void); +static void set_starting_view(int index, win_window_info *window, const char *view); + +static void constrain_to_aspect_ratio(win_window_info *window, RECT *rect, int adjustment); +static void get_min_bounds(win_window_info *window, RECT *bounds, int constrain); +static void get_max_bounds(win_window_info *window, RECT *bounds, int constrain); +static void update_minmax_state(win_window_info *window); +static void minimize_window(win_window_info *window); +static void maximize_window(win_window_info *window); + +static void adjust_window_position_after_major_change(win_window_info *window); +static void set_fullscreen(win_window_info *window, int fullscreen); + + +// temporary hacks +#if LOG_THREADS +struct _mtlog +{ + osd_ticks_t timestamp; + const char *event; +}; + +static struct _mtlog mtlog[100000]; +static volatile LONG mtlogindex; + +void mtlog_add(const char *event) +{ + int index = InterlockedIncrement((LONG *) &mtlogindex) - 1; + if (index < ARRAY_LENGTH(mtlog)) + { + mtlog[index].timestamp = osd_ticks(); + mtlog[index].event = event; + } +} + +static void mtlog_dump(void) +{ + osd_ticks_t cps = osd_ticks_per_second(); + osd_ticks_t last = mtlog[0].timestamp * 1000000 / cps; + int i; + + FILE *f = fopen("mt.log", "w"); + for (i = 0; i < mtlogindex; i++) + { + osd_ticks_t curr = mtlog[i].timestamp * 1000000 / cps; + fprintf(f, "%20I64d %10I64d %s\n", curr, curr - last, mtlog[i].event); + last = curr; + } + fclose(f); +} +#else +void mtlog_add(const char *event) { } +#endif + + + +//============================================================ +// winwindow_init +// (main thread) +//============================================================ + +void winwindow_init(running_machine *machine) +{ + size_t temp; + + // determine if we are using multithreading or not + multithreading_enabled = options_get_bool(mame_options(), WINOPTION_MULTITHREADING); + + // get the main thread ID before anything else + main_threadid = GetCurrentThreadId(); + + // ensure we get called on the way out + add_exit_callback(machine, winwindow_exit); + + // set up window class and register it + create_window_class(); + + // create an event to signal UI pausing + ui_pause_event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!ui_pause_event) + fatalerror("Failed to create pause event"); + + // if multithreading, create a thread to run the windows + if (multithreading_enabled) + { + // create an event to signal when the window thread is ready + window_thread_ready_event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!window_thread_ready_event) + fatalerror("Failed to create window thread ready event"); + + // create a thread to run the windows from + temp = _beginthreadex(NULL, 0, thread_entry, NULL, 0, (unsigned *)&window_threadid); + window_thread = (HANDLE)temp; + if (window_thread == NULL) + fatalerror("Failed to create window thread"); + + // set the thread priority equal to the main MAME thread + SetThreadPriority(window_thread, GetThreadPriority(GetCurrentThread())); + } + + // otherwise, treat the window thread as the main thread + else + { + window_thread = GetCurrentThread(); + window_threadid = main_threadid; + } + + // initialize the drawers + if (video_config.mode == VIDEO_MODE_D3D) + { + if (drawd3d_init(&draw)) + video_config.mode = VIDEO_MODE_GDI; + } + if (video_config.mode == VIDEO_MODE_DDRAW) + { + if (drawdd_init(&draw)) + video_config.mode = VIDEO_MODE_GDI; + } + if (video_config.mode == VIDEO_MODE_GDI) + drawgdi_init(&draw); + if (video_config.mode == VIDEO_MODE_NONE) + drawnone_init(&draw); + + // set up the window list + last_window_ptr = &win_window_list; +} + + + +//============================================================ +// winwindow_exit +// (main thread) +//============================================================ + +static void winwindow_exit(running_machine *machine) +{ + assert(GetCurrentThreadId() == main_threadid); + + // free all the windows + while (win_window_list != NULL) + { + win_window_info *temp = win_window_list; + win_window_list = temp->next; + winwindow_video_window_destroy(temp); + } + + // kill the drawers + (*draw.exit)(); + + // if we're multithreaded, clean up the window thread + if (multithreading_enabled) + { + PostThreadMessage(window_threadid, WM_USER_SELF_TERMINATE, 0, 0); + WaitForSingleObject(window_thread, INFINITE); + +#if (LOG_THREADS) + mtlog_dump(); +#endif + } + + // kill the UI pause event + if (ui_pause_event) + CloseHandle(ui_pause_event); + + // kill the window thread ready event + if (window_thread_ready_event) + CloseHandle(window_thread_ready_event); + + // if we hid the cursor during the emulation, show it + while (ShowCursor(TRUE) < 0) ; +} + + + +//============================================================ +// winwindow_process_events_periodic +// (main thread) +//============================================================ + +void winwindow_process_events_periodic(void) +{ + DWORD currticks = GetTickCount(); + + assert(GetCurrentThreadId() == main_threadid); + + // update once every 1/8th of a second + if (currticks - last_event_check < 1000 / 8) + return; + winwindow_process_events(TRUE); +} + + + +//============================================================ +// is_mame_window +//============================================================ + +static BOOL is_mame_window(HWND hwnd) +{ + win_window_info *window; + + for (window = win_window_list; window != NULL; window = window->next) + if (window->hwnd == hwnd) + return TRUE; + + return FALSE; +} + + + +//============================================================ +// winwindow_process_events +// (main thread) +//============================================================ + +void winwindow_process_events(int ingame) +{ + MSG message; + + assert(GetCurrentThreadId() == main_threadid); + + // if we're running, disable some parts of the debugger +#if defined(MAME_DEBUG) + if (ingame) + debugwin_update_during_game(); +#endif + + // remember the last time we did this + last_event_check = GetTickCount(); + + do + { + // if we are paused, lets wait for a message + if (ui_temp_pause > 0) + WaitMessage(); + + // loop over all messages in the queue + while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) + { + int dispatch = TRUE; + + if (message.hwnd == NULL || is_mame_window(message.hwnd)) + { + switch (message.message) + { + // ignore keyboard messages + case WM_SYSKEYUP: + case WM_SYSKEYDOWN: +#ifndef MESS + case WM_KEYUP: + case WM_KEYDOWN: + case WM_CHAR: +#endif + dispatch = FALSE; + break; + + // forward mouse button downs to the input system + case WM_LBUTTONDOWN: + wininput_handle_mouse_button(0, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_RBUTTONDOWN: + wininput_handle_mouse_button(1, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_MBUTTONDOWN: + wininput_handle_mouse_button(2, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_XBUTTONDOWN: + wininput_handle_mouse_button(3, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + // forward mouse button ups to the input system + case WM_LBUTTONUP: + wininput_handle_mouse_button(0, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_RBUTTONUP: + wininput_handle_mouse_button(1, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_MBUTTONUP: + wininput_handle_mouse_button(2, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_XBUTTONUP: + wininput_handle_mouse_button(3, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + } + } + + // dispatch if necessary + if (dispatch) + winwindow_dispatch_message(&message); + } + } while (ui_temp_pause > 0); + + // update the cursor state after processing events + winwindow_update_cursor_state(); +} + + + +//============================================================ +// winwindow_dispatch_message +// (main thread) +//============================================================ + +void winwindow_dispatch_message(MSG *message) +{ + assert(GetCurrentThreadId() == main_threadid); + + // dispatch our special communication messages + switch (message->message) + { + // special case for quit + case WM_QUIT: + mame_schedule_exit(Machine); + break; + + // temporary pause from the window thread + case WM_USER_UI_TEMP_PAUSE: + winwindow_ui_pause_from_main_thread(message->wParam); + break; + + // execute arbitrary function + case WM_USER_EXEC_FUNC: + { + void (*func)(void *) = (void (*)(void *)) message->wParam; + void *param = (void *) message->lParam; + func(param); + } + break; + + // everything else dispatches normally + default: + TranslateMessage(message); + DispatchMessage(message); + break; + } +} + + + +//============================================================ +// winwindow_toggle_full_screen +// (main thread) +//============================================================ + +void winwindow_toggle_full_screen(void) +{ + win_window_info *window; + + assert(GetCurrentThreadId() == main_threadid); + +#ifdef MAME_DEBUG + // if we are in debug mode, never go full screen + if (options_get_bool(mame_options(), OPTION_DEBUG)) + return; +#endif + + // toggle the window mode + video_config.windowed = !video_config.windowed; + + // iterate over windows and toggle their fullscreen state + for (window = win_window_list; window != NULL; window = window->next) + SendMessage(window->hwnd, WM_USER_SET_FULLSCREEN, !video_config.windowed, 0); + SetForegroundWindow(win_window_list->hwnd); +} + + + +//============================================================ +// winwindow_has_focus +// (main or window thread) +//============================================================ + +BOOL winwindow_has_focus(void) +{ + HWND focuswnd = GetFocus(); + win_window_info *window; + + // see if one of the video windows has focus + for (window = win_window_list; window != NULL; window = window->next) + if (focuswnd == window->hwnd) + return TRUE; + + return FALSE; +} + + +//============================================================ +// winwindow_update_cursor_state +// (main thread) +//============================================================ + +void winwindow_update_cursor_state(void) +{ + static POINT saved_cursor_pos = { -1, -1 }; + + assert(GetCurrentThreadId() == main_threadid); + + // if we should hide the mouse, then do it + // rules are: + // 1. we must have focus before hiding the cursor + // 2. we also hide the cursor in full screen mode and when the window doesn't have a menu + // 3. we also hide the cursor in windowed mode if we're not paused and + // the input system requests it + if (winwindow_has_focus() && ((!video_config.windowed && !win_has_menu(win_window_list)) || (!mame_is_paused(Machine) && wininput_should_hide_mouse()))) + { + win_window_info *window = win_window_list; + RECT bounds; + + // hide cursor + while (ShowCursor(FALSE) >= -1) ; + ShowCursor(TRUE); + + // store the cursor position + GetCursorPos(&saved_cursor_pos); + + // clip cursor to game video window + GetClientRect(window->hwnd, &bounds); + ClientToScreen(window->hwnd, &((POINT *)&bounds)[0]); + ClientToScreen(window->hwnd, &((POINT *)&bounds)[1]); + ClipCursor(&bounds); + } + else + { + // show cursor + while (ShowCursor(TRUE) < 1) ; + ShowCursor(FALSE); + + // allow cursor to move freely + ClipCursor(NULL); + if (saved_cursor_pos.x != -1 || saved_cursor_pos.y != -1) + { + SetCursorPos(saved_cursor_pos.x, saved_cursor_pos.y); + saved_cursor_pos.x = saved_cursor_pos.y = -1; + } + } +} + + + +//============================================================ +// winwindow_video_window_create +// (main thread) +//============================================================ + +void winwindow_video_window_create(int index, win_monitor_info *monitor, const win_window_config *config) +{ + win_window_info *window, *win; + char option[20]; + + assert(GetCurrentThreadId() == main_threadid); + + // allocate a new window object + window = malloc_or_die(sizeof(*window)); + memset(window, 0, sizeof(*window)); + window->maxwidth = config->width; + window->maxheight = config->height; + window->refresh = config->refresh; + window->monitor = monitor; + window->fullscreen = !video_config.windowed; + + // see if we are safe for fullscreen + window->fullscreen_safe = TRUE; + for (win = win_window_list; win != NULL; win = win->next) + if (win->monitor == monitor) + window->fullscreen_safe = FALSE; + + // add us to the list + *last_window_ptr = window; + last_window_ptr = &window->next; + + // create a lock that we can use to skip blitting + window->render_lock = osd_lock_alloc(); + + // load the layout + window->target = render_target_alloc(NULL, 0); + if (window->target == NULL) + fatalerror("Error creating render target for window %d", index); + + // set the specific view + sprintf(option, "view%d", index); + set_starting_view(index, window, options_get_string(mame_options(), option)); + + // remember the current values in case they change + window->targetview = render_target_get_view(window->target); + window->targetorient = render_target_get_orientation(window->target); + window->targetlayerconfig = render_target_get_layer_config(window->target); + + // make the window title + if (video_config.numscreens == 1) + sprintf(window->title, APPNAME ": %s [%s]", Machine->gamedrv->description, Machine->gamedrv->name); + else + sprintf(window->title, APPNAME ": %s [%s] - Screen %d", Machine->gamedrv->description, Machine->gamedrv->name, index); + + // set the initial maximized state + window->startmaximized = options_get_bool(mame_options(), WINOPTION_MAXIMIZE); + + // finish the window creation on the window thread + if (multithreading_enabled) + { + // wait until the window thread is ready to respond to events + WaitForSingleObject(window_thread_ready_event, INFINITE); + + PostThreadMessage(window_threadid, WM_USER_FINISH_CREATE_WINDOW, 0, (LPARAM)window); + while (window->init_state == 0) + Sleep(1); + } + else + window->init_state = complete_create(window) ? -1 : 1; + + // handle error conditions + if (window->init_state == -1) + fatalerror("Unable to complete window creation"); +} + + + +//============================================================ +// winwindow_video_window_destroy +// (main thread) +//============================================================ + +static void winwindow_video_window_destroy(win_window_info *window) +{ + win_window_info **prevptr; + + assert(GetCurrentThreadId() == main_threadid); + + // remove us from the list + for (prevptr = &win_window_list; *prevptr != NULL; prevptr = &(*prevptr)->next) + if (*prevptr == window) + { + *prevptr = window->next; + break; + } + + // destroy the window + if (window->hwnd != NULL) + SendMessage(window->hwnd, WM_USER_SELF_TERMINATE, 0, 0); + + // free the render target + if (window->target != NULL) + render_target_free(window->target); + + // free the lock + osd_lock_free(window->render_lock); + + // free the window itself + free(window); +} + + + +//============================================================ +// winwindow_video_window_update +// (main thread) +//============================================================ + +void winwindow_video_window_update(win_window_info *window) +{ + int targetview, targetorient, targetlayerconfig; + + assert(GetCurrentThreadId() == main_threadid); + + mtlog_add("winwindow_video_window_update: begin"); + + // see if the target has changed significantly in window mode + targetview = render_target_get_view(window->target); + targetorient = render_target_get_orientation(window->target); + targetlayerconfig = render_target_get_layer_config(window->target); + if (targetview != window->targetview || targetorient != window->targetorient || targetlayerconfig != window->targetlayerconfig) + { + window->targetview = targetview; + window->targetorient = targetorient; + window->targetlayerconfig = targetlayerconfig; + + // in window mode, reminimize/maximize + if (!window->fullscreen) + { + if (window->isminimized) + SendMessage(window->hwnd, WM_USER_SET_MINSIZE, 0, 0); + if (window->ismaximized) + SendMessage(window->hwnd, WM_USER_SET_MAXSIZE, 0, 0); + } + } + + // if we're visible and running and not in the middle of a resize, draw + if (window->hwnd != NULL && window->target != NULL) + { + int got_lock = TRUE; + + mtlog_add("winwindow_video_window_update: try lock"); + + // only block if we're throttled + if (video_get_throttle() || timeGetTime() - last_update_time > 250) + osd_lock_acquire(window->render_lock); + else + got_lock = osd_lock_try(window->render_lock); + + // only render if we were able to get the lock + if (got_lock) + { + const render_primitive_list *primlist; + + mtlog_add("winwindow_video_window_update: got lock"); + + // don't hold the lock; we just used it to see if rendering was still happening + osd_lock_release(window->render_lock); + + // ensure the target bounds are up-to-date, and then get the primitives + primlist = (*draw.window_get_primitives)(window); + + // post a redraw request with the primitive list as a parameter + last_update_time = timeGetTime(); + mtlog_add("winwindow_video_window_update: PostMessage start"); + if (multithreading_enabled) + PostMessage(window->hwnd, WM_USER_REDRAW, 0, (LPARAM)primlist); + else + SendMessage(window->hwnd, WM_USER_REDRAW, 0, (LPARAM)primlist); + mtlog_add("winwindow_video_window_update: PostMessage end"); + } + } + + mtlog_add("winwindow_video_window_update: end"); +} + + + +//============================================================ +// winwindow_video_window_monitor +// (window thread) +//============================================================ + +win_monitor_info *winwindow_video_window_monitor(win_window_info *window, const RECT *proposed) +{ + win_monitor_info *monitor; + + // in window mode, find the nearest + if (!window->fullscreen) + { + if (proposed != NULL) + monitor = winvideo_monitor_from_handle(MonitorFromRect(proposed, MONITOR_DEFAULTTONEAREST)); + else + monitor = winvideo_monitor_from_handle(MonitorFromWindow(window->hwnd, MONITOR_DEFAULTTONEAREST)); + } + + // in full screen, just use the configured monitor + else + monitor = window->monitor; + + // make sure we're up-to-date + winvideo_monitor_refresh(monitor); + return monitor; +} + + + +//============================================================ +// create_window_class +// (main thread) +//============================================================ + +static void create_window_class(void) +{ + static int classes_created = FALSE; + + assert(GetCurrentThreadId() == main_threadid); + + if (!classes_created) + { + WNDCLASS wc = { 0 }; + + // initialize the description of the window class + wc.lpszClassName = TEXT("MAME"); + wc.hInstance = GetModuleHandle(NULL); +#ifdef MESS + wc.lpfnWndProc = win_mess_window_proc; +#else + wc.lpfnWndProc = winwindow_video_window_proc; +#endif + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + + // register the class; fail if we can't + if (!RegisterClass(&wc)) + fatalerror("Failed to create window class"); + classes_created = TRUE; + } +} + + + +//============================================================ +// set_starting_view +// (main thread) +//============================================================ + +static void set_starting_view(int index, win_window_info *window, const char *view) +{ + const char *defview = options_get_string(mame_options(), WINOPTION_VIEW); + int viewindex = -1; + + assert(GetCurrentThreadId() == main_threadid); + + // choose non-auto over auto + if (strcmp(view, "auto") == 0 && strcmp(defview, "auto") != 0) + view = defview; + + // auto view just selects the nth view + if (strcmp(view, "auto") != 0) + { + // scan for a matching view name + for (viewindex = 0; ; viewindex++) + { + const char *name = render_target_get_view_name(window->target, viewindex); + + // stop scanning if we hit NULL + if (name == NULL) + { + viewindex = -1; + break; + } + if (mame_strnicmp(name, view, strlen(view)) == 0) + break; + } + } + + // if we don't have a match, default to the nth view + if (viewindex == -1) + { + int scrcount; + + // count the number of screens + for (scrcount = 0; Machine->drv->screen[scrcount].tag != NULL; scrcount++) ; + + // if we have enough screens to be one per monitor, assign in order + if (video_config.numscreens >= scrcount) + { + // find the first view with this screen and this screen only + for (viewindex = 0; ; viewindex++) + { + UINT32 viewscreens = render_target_get_view_screens(window->target, viewindex); + if (viewscreens == (1 << index)) + break; + if (viewscreens == 0) + { + viewindex = -1; + break; + } + } + } + + // otherwise, find the first view that has all the screens + if (viewindex == -1) + { + for (viewindex = 0; ; viewindex++) + { + UINT32 viewscreens = render_target_get_view_screens(window->target, viewindex); + if (viewscreens == (1 << scrcount) - 1) + break; + if (viewscreens == 0) + break; + } + } + } + + // make sure it's a valid view + if (render_target_get_view_name(window->target, viewindex) == NULL) + viewindex = 0; + + // set the view + render_target_set_view(window->target, viewindex); +} + + + +//============================================================ +// winwindow_ui_pause_from_main_thread +// (main thread) +//============================================================ + +void winwindow_ui_pause_from_main_thread(int pause) +{ + int old_temp_pause = ui_temp_pause; + + assert(GetCurrentThreadId() == main_threadid); + + // if we're pausing, increment the pause counter + if (pause) + { + // if we're the first to pause, we have to actually initiate it + if (ui_temp_pause++ == 0) + { + // only call mame_pause if we weren't already paused due to some external reason + ui_temp_was_paused = mame_is_paused(Machine); + if (!ui_temp_was_paused) + mame_pause(Machine, TRUE); + + SetEvent(ui_pause_event); + } + } + + // if we're resuming, decrement the pause counter + else + { + // if we're the last to resume, unpause MAME + if (--ui_temp_pause == 0) + { + // but only do it if we were the ones who initiated it + if (!ui_temp_was_paused) + mame_pause(Machine, FALSE); + + ResetEvent(ui_pause_event); + } + } + + if (LOG_TEMP_PAUSE) + logerror("winwindow_ui_pause_from_main_thread(): %d --> %d\n", old_temp_pause, ui_temp_pause); +} + + + +//============================================================ +// winwindow_ui_pause_from_window_thread +// (window thread) +//============================================================ + +void winwindow_ui_pause_from_window_thread(int pause) +{ + assert(GetCurrentThreadId() == window_threadid); + + // if we're multithreaded, we have to request a pause on the main thread + if (multithreading_enabled) + { + // request a pause from the main thread + PostThreadMessage(main_threadid, WM_USER_UI_TEMP_PAUSE, pause, 0); + + // if we're pausing, block until it happens + if (pause) + WaitForSingleObject(ui_pause_event, INFINITE); + } + + // otherwise, we just do it directly + else + winwindow_ui_pause_from_main_thread(pause); +} + + + +//============================================================ +// winwindow_ui_exec_on_main_thread +// (window thread) +//============================================================ + +void winwindow_ui_exec_on_main_thread(void (*func)(void *), void *param) +{ + assert(GetCurrentThreadId() == window_threadid); + + // if we're multithreaded, we have to request a pause on the main thread + if (multithreading_enabled) + { + // request a pause from the main thread + PostThreadMessage(main_threadid, WM_USER_EXEC_FUNC, (WPARAM) func, (LPARAM) param); + } + + // otherwise, we just do it directly + else + (*func)(param); +} + + + +//============================================================ +// winwindow_ui_is_paused +//============================================================ + +int winwindow_ui_is_paused(void) +{ + return mame_is_paused(Machine) && ui_temp_was_paused; +} + + + +//============================================================ +// wnd_extra_width +// (window thread) +//============================================================ + +INLINE int wnd_extra_width(win_window_info *window) +{ + RECT temprect = { 100, 100, 200, 200 }; + if (window->fullscreen) + return 0; + AdjustWindowRectEx(&temprect, WINDOW_STYLE, win_has_menu(window), WINDOW_STYLE_EX); + return rect_width(&temprect) - 100; +} + + + +//============================================================ +// wnd_extra_height +// (window thread) +//============================================================ + +INLINE int wnd_extra_height(win_window_info *window) +{ + RECT temprect = { 100, 100, 200, 200 }; + if (window->fullscreen) + return 0; + AdjustWindowRectEx(&temprect, WINDOW_STYLE, win_has_menu(window), WINDOW_STYLE_EX); + return rect_height(&temprect) - 100; +} + + + +//============================================================ +// thread_entry +// (window thread) +//============================================================ + +static unsigned __stdcall thread_entry(void *param) +{ + MSG message; + + // make a bogus user call to make us a message thread + PeekMessage(&message, NULL, 0, 0, PM_NOREMOVE); + + // attach our input to the main thread + AttachThreadInput(main_threadid, window_threadid, TRUE); + + // signal to the main thread that we are ready to receive events + SetEvent(window_thread_ready_event); + + // run the message pump + while (GetMessage(&message, NULL, 0, 0)) + { + int dispatch = TRUE; + + if ((message.hwnd == NULL) || is_mame_window(message.hwnd)) + { + switch (message.message) + { + // ignore input messages here + case WM_SYSKEYUP: + case WM_SYSKEYDOWN: +#ifndef MESS + case WM_KEYUP: + case WM_KEYDOWN: + case WM_CHAR: +#endif // MESS + dispatch = FALSE; + break; + + // forward mouse button downs to the input system + case WM_LBUTTONDOWN: + wininput_handle_mouse_button(0, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_RBUTTONDOWN: + wininput_handle_mouse_button(1, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_MBUTTONDOWN: + wininput_handle_mouse_button(2, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_XBUTTONDOWN: + wininput_handle_mouse_button(3, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + // forward mouse button ups to the input system + case WM_LBUTTONUP: + wininput_handle_mouse_button(0, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_RBUTTONUP: + wininput_handle_mouse_button(1, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_MBUTTONUP: + wininput_handle_mouse_button(2, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + case WM_XBUTTONUP: + wininput_handle_mouse_button(3, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = FALSE; + break; + + // a terminate message to the thread posts a quit + case WM_USER_SELF_TERMINATE: + PostQuitMessage(0); + dispatch = FALSE; + break; + + // handle the "complete create" message + case WM_USER_FINISH_CREATE_WINDOW: + { + win_window_info *window = (win_window_info *)message.lParam; + window->init_state = complete_create(window) ? -1 : 1; + dispatch = FALSE; + break; + } + } + } + + // dispatch if necessary + if (dispatch) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + } + return 0; +} + + + +//============================================================ +// complete_create +// (window thread) +//============================================================ + +static int complete_create(win_window_info *window) +{ + RECT monitorbounds, client; + int tempwidth, tempheight; + HMENU menu = NULL; + HDC dc; + + assert(GetCurrentThreadId() == window_threadid); + + // get the monitor bounds + monitorbounds = window->monitor->info.rcMonitor; + + // create the window menu if needed +#if HAS_WINDOW_MENU + if (win_create_menu(&menu)) + return 1; +#endif + + // create the window, but don't show it yet + window->hwnd = win_create_window_ex_utf8( + window->fullscreen ? FULLSCREEN_STYLE_EX : WINDOW_STYLE_EX, + "MAME", + window->title, + window->fullscreen ? FULLSCREEN_STYLE : WINDOW_STYLE, + monitorbounds.left + 20, monitorbounds.top + 20, + monitorbounds.left + 100, monitorbounds.top + 100, + NULL,//(win_window_list != NULL) ? win_window_list->hwnd : NULL, + menu, + GetModuleHandle(NULL), + NULL); + if (window->hwnd == NULL) + return 1; + + // set a pointer back to us + SetWindowLongPtr(window->hwnd, GWLP_USERDATA, (LONG_PTR)window); + + // skip the positioning stuff for -video none */ + if (video_config.mode == VIDEO_MODE_NONE) + return 0; + + // adjust the window position to the initial width/height + tempwidth = (window->maxwidth != 0) ? window->maxwidth : 640; + tempheight = (window->maxheight != 0) ? window->maxheight : 480; + SetWindowPos(window->hwnd, NULL, monitorbounds.left + 20, monitorbounds.top + 20, + monitorbounds.left + tempwidth + wnd_extra_width(window), + monitorbounds.top + tempheight + wnd_extra_height(window), + SWP_NOZORDER); + + // maximum or minimize as appropriate + if (window->startmaximized) + maximize_window(window); + else + minimize_window(window); + adjust_window_position_after_major_change(window); + + // show the window + if (!window->fullscreen || window->fullscreen_safe) + { + // finish off by trying to initialize DirectX; if we fail, ignore it + if ((*draw.window_init)(window)) + return 1; + ShowWindow(window->hwnd, SW_SHOW); + } + + // clear the window + dc = GetDC(window->hwnd); + GetClientRect(window->hwnd, &client); + FillRect(dc, &client, (HBRUSH)GetStockObject(BLACK_BRUSH)); + ReleaseDC(window->hwnd, dc); + return 0; +} + + + +//============================================================ +// winwindow_video_window_proc +// (window thread) +//============================================================ + +LRESULT CALLBACK winwindow_video_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + LONG_PTR ptr = GetWindowLongPtr(wnd, GWLP_USERDATA); + win_window_info *window = (win_window_info *)ptr; + + // we may get called before SetWindowLongPtr is called + if (window != NULL) + { + assert(GetCurrentThreadId() == window_threadid); + update_minmax_state(window); + } + + // handle a few messages + switch (message) + { + // paint: redraw the last bitmap + case WM_PAINT: + { + PAINTSTRUCT pstruct; + HDC hdc = BeginPaint(wnd, &pstruct); + draw_video_contents(window, hdc, TRUE); + if (win_has_menu(window)) + DrawMenuBar(window->hwnd); + EndPaint(wnd, &pstruct); + break; + } + + // non-client paint: punt if full screen + case WM_NCPAINT: + if (!window->fullscreen || HAS_WINDOW_MENU) + return DefWindowProc(wnd, message, wparam, lparam); + break; + + // input: handle the raw input + case WM_INPUT: + wininput_handle_raw((HRAWINPUT)lparam); + break; + + // syskeys - ignore + case WM_SYSKEYUP: + case WM_SYSKEYDOWN: + break; + + // pause the system when we start a menu or resize + case WM_ENTERSIZEMOVE: + window->resize_state = RESIZE_STATE_RESIZING; + case WM_ENTERMENULOOP: + winwindow_ui_pause_from_window_thread(TRUE); + break; + + // unpause the system when we stop a menu or resize and force a redraw + case WM_EXITSIZEMOVE: + window->resize_state = RESIZE_STATE_PENDING; + case WM_EXITMENULOOP: + winwindow_ui_pause_from_window_thread(FALSE); + InvalidateRect(wnd, NULL, FALSE); + break; + + // get min/max info: set the minimum window size + case WM_GETMINMAXINFO: + { + MINMAXINFO *minmax = (MINMAXINFO *)lparam; + minmax->ptMinTrackSize.x = MIN_WINDOW_DIM; + minmax->ptMinTrackSize.y = MIN_WINDOW_DIM; + break; + } + + // sizing: constrain to the aspect ratio unless control key is held down + case WM_SIZING: + { + RECT *rect = (RECT *)lparam; + if (video_config.keepaspect && !(GetAsyncKeyState(VK_CONTROL) & 0x8000)) + constrain_to_aspect_ratio(window, rect, wparam); + InvalidateRect(wnd, NULL, FALSE); + break; + } + + // syscommands: catch win_start_maximized + case WM_SYSCOMMAND: + { + // prevent screensaver or monitor power events + if (wparam == SC_MONITORPOWER || wparam == SC_SCREENSAVE) + return 1; + + // most SYSCOMMANDs require us to invalidate the window + InvalidateRect(wnd, NULL, FALSE); + + // handle maximize + if ((wparam & 0xfff0) == SC_MAXIMIZE) + { + update_minmax_state(window); + if (window->ismaximized) + minimize_window(window); + else + maximize_window(window); + break; + } + return DefWindowProc(wnd, message, wparam, lparam); + } + + // track whether we are in the foreground + case WM_ACTIVATEAPP: + in_background = !wparam; + break; + + // close: cause MAME to exit + case WM_CLOSE: + if (multithreading_enabled) + PostThreadMessage(main_threadid, WM_QUIT, 0, 0); + else + mame_schedule_exit(Machine); + break; + + // destroy: clean up all attached rendering bits and NULL out our hwnd + case WM_DESTROY: + (*draw.window_destroy)(window); + window->hwnd = NULL; + return DefWindowProc(wnd, message, wparam, lparam); + + // self redraw: draw ourself in a non-painty way + case WM_USER_REDRAW: + { + HDC hdc = GetDC(wnd); + + mtlog_add("winwindow_video_window_proc: WM_USER_REDRAW begin"); + window->primlist = (const render_primitive_list *)lparam; + draw_video_contents(window, hdc, FALSE); + mtlog_add("winwindow_video_window_proc: WM_USER_REDRAW end"); + + ReleaseDC(wnd, hdc); + break; + } + + // self destruct + case WM_USER_SELF_TERMINATE: + DestroyWindow(window->hwnd); + break; + + // fullscreen set + case WM_USER_SET_FULLSCREEN: + set_fullscreen(window, wparam); + break; + + // minimum size set + case WM_USER_SET_MINSIZE: + minimize_window(window); + break; + + // maximum size set + case WM_USER_SET_MAXSIZE: + maximize_window(window); + break; + + // set focus: if we're not the primary window, switch back + // commented out ATM because this prevents us from resizing secondary windows +// case WM_SETFOCUS: +// if (window != win_window_list && win_window_list != NULL) +// SetFocus(win_window_list->hwnd); +// break; + + // everything else: defaults + default: + return DefWindowProc(wnd, message, wparam, lparam); + } + + return 0; +} + + + +//============================================================ +// draw_video_contents +// (window thread) +//============================================================ + +static void draw_video_contents(win_window_info *window, HDC dc, int update) +{ + assert(GetCurrentThreadId() == window_threadid); + + mtlog_add("draw_video_contents: begin"); + + mtlog_add("draw_video_contents: render lock acquire"); + osd_lock_acquire(window->render_lock); + mtlog_add("draw_video_contents: render lock acquired"); + + // if we're iconic, don't bother + if (window->hwnd != NULL && !IsIconic(window->hwnd)) + { + // if no bitmap, just fill + if (window->primlist == NULL) + { + RECT fill; + GetClientRect(window->hwnd, &fill); + FillRect(dc, &fill, (HBRUSH)GetStockObject(BLACK_BRUSH)); + } + + // otherwise, render with our drawing system + else + { + (*draw.window_draw)(window, dc, update); + mtlog_add("draw_video_contents: drawing finished"); + } + } + + osd_lock_release(window->render_lock); + mtlog_add("draw_video_contents: render lock released"); + + mtlog_add("draw_video_contents: end"); +} + + + +//============================================================ +// constrain_to_aspect_ratio +// (window thread) +//============================================================ + +static void constrain_to_aspect_ratio(win_window_info *window, RECT *rect, int adjustment) +{ + win_monitor_info *monitor = winwindow_video_window_monitor(window, rect); + INT32 extrawidth = wnd_extra_width(window); + INT32 extraheight = wnd_extra_height(window); + INT32 propwidth, propheight; + INT32 minwidth, minheight; + INT32 maxwidth, maxheight; + INT32 viswidth, visheight; + INT32 adjwidth, adjheight; + float pixel_aspect; + + assert(GetCurrentThreadId() == window_threadid); + + // get the pixel aspect ratio for the target monitor + pixel_aspect = winvideo_monitor_get_aspect(monitor); + + // determine the proposed width/height + propwidth = rect_width(rect) - extrawidth; + propheight = rect_height(rect) - extraheight; + + // based on which edge we are adjusting, take either the width, height, or both as gospel + // and scale to fit using that as our parameter + switch (adjustment) + { + case WMSZ_BOTTOM: + case WMSZ_TOP: + render_target_compute_visible_area(window->target, 10000, propheight, pixel_aspect, render_target_get_orientation(window->target), &propwidth, &propheight); + break; + + case WMSZ_LEFT: + case WMSZ_RIGHT: + render_target_compute_visible_area(window->target, propwidth, 10000, pixel_aspect, render_target_get_orientation(window->target), &propwidth, &propheight); + break; + + default: + render_target_compute_visible_area(window->target, propwidth, propheight, pixel_aspect, render_target_get_orientation(window->target), &propwidth, &propheight); + break; + } + + // get the minimum width/height for the current layout + render_target_get_minimum_size(window->target, &minwidth, &minheight); + + // clamp against the absolute minimum + propwidth = MAX(propwidth, MIN_WINDOW_DIM); + propheight = MAX(propheight, MIN_WINDOW_DIM); + + // clamp against the minimum width and height + propwidth = MAX(propwidth, minwidth); + propheight = MAX(propheight, minheight); + + // clamp against the maximum (fit on one screen for full screen mode) + if (window->fullscreen) + { + maxwidth = rect_width(&monitor->info.rcMonitor) - extrawidth; + maxheight = rect_height(&monitor->info.rcMonitor) - extraheight; + } + else + { + maxwidth = rect_width(&monitor->info.rcWork) - extrawidth; + maxheight = rect_height(&monitor->info.rcWork) - extraheight; + + // further clamp to the maximum width/height in the window + if (window->maxwidth != 0) + maxwidth = MIN(maxwidth, window->maxwidth + extrawidth); + if (window->maxheight != 0) + maxheight = MIN(maxheight, window->maxheight + extraheight); + } + + // clamp to the maximum + propwidth = MIN(propwidth, maxwidth); + propheight = MIN(propheight, maxheight); + + // compute the visible area based on the proposed rectangle + render_target_compute_visible_area(window->target, propwidth, propheight, pixel_aspect, render_target_get_orientation(window->target), &viswidth, &visheight); + + // compute the adjustments we need to make + adjwidth = (viswidth + extrawidth) - rect_width(rect); + adjheight = (visheight + extraheight) - rect_height(rect); + + // based on which corner we're adjusting, constrain in different ways + switch (adjustment) + { + case WMSZ_BOTTOM: + case WMSZ_BOTTOMRIGHT: + case WMSZ_RIGHT: + rect->right += adjwidth; + rect->bottom += adjheight; + break; + + case WMSZ_BOTTOMLEFT: + rect->left -= adjwidth; + rect->bottom += adjheight; + break; + + case WMSZ_LEFT: + case WMSZ_TOPLEFT: + case WMSZ_TOP: + rect->left -= adjwidth; + rect->top -= adjheight; + break; + + case WMSZ_TOPRIGHT: + rect->right += adjwidth; + rect->top -= adjheight; + break; + } +} + + + +//============================================================ +// get_min_bounds +// (window thread) +//============================================================ + +static void get_min_bounds(win_window_info *window, RECT *bounds, int constrain) +{ + INT32 minwidth, minheight; + + assert(GetCurrentThreadId() == window_threadid); + + // get the minimum target size + render_target_get_minimum_size(window->target, &minwidth, &minheight); + + // expand to our minimum dimensions + if (minwidth < MIN_WINDOW_DIM) + minwidth = MIN_WINDOW_DIM; + if (minheight < MIN_WINDOW_DIM) + minheight = MIN_WINDOW_DIM; + + // account for extra window stuff + minwidth += wnd_extra_width(window); + minheight += wnd_extra_height(window); + + // if we want it constrained, figure out which one is larger + if (constrain) + { + RECT test1, test2; + + // first constrain with no height limit + test1.top = test1.left = 0; + test1.right = minwidth; + test1.bottom = 10000; + constrain_to_aspect_ratio(window, &test1, WMSZ_BOTTOMRIGHT); + + // then constrain with no width limit + test2.top = test2.left = 0; + test2.right = 10000; + test2.bottom = minheight; + constrain_to_aspect_ratio(window, &test2, WMSZ_BOTTOMRIGHT); + + // pick the larger + if (rect_width(&test1) > rect_width(&test2)) + { + minwidth = rect_width(&test1); + minheight = rect_height(&test1); + } + else + { + minwidth = rect_width(&test2); + minheight = rect_height(&test2); + } + } + + // get the window rect + GetWindowRect(window->hwnd, bounds); + + // now adjust + bounds->right = bounds->left + minwidth; + bounds->bottom = bounds->top + minheight; +} + + + +//============================================================ +// get_max_bounds +// (window thread) +//============================================================ + +static void get_max_bounds(win_window_info *window, RECT *bounds, int constrain) +{ + RECT maximum; + + assert(GetCurrentThreadId() == window_threadid); + + // compute the maximum client area + winvideo_monitor_refresh(window->monitor); + maximum = window->monitor->info.rcWork; + + // clamp to the window's max + if (window->maxwidth != 0) + { + int temp = window->maxwidth + wnd_extra_width(window); + if (temp < rect_width(&maximum)) + maximum.right = maximum.left + temp; + } + if (window->maxheight != 0) + { + int temp = window->maxheight + wnd_extra_height(window); + if (temp < rect_height(&maximum)) + maximum.bottom = maximum.top + temp; + } + + // constrain to fit + if (constrain) + constrain_to_aspect_ratio(window, &maximum, WMSZ_BOTTOMRIGHT); + else + { + maximum.right -= wnd_extra_width(window); + maximum.bottom -= wnd_extra_height(window); + } + + // center within the work area + bounds->left = window->monitor->info.rcWork.left + (rect_width(&window->monitor->info.rcWork) - rect_width(&maximum)) / 2; + bounds->top = window->monitor->info.rcWork.top + (rect_height(&window->monitor->info.rcWork) - rect_height(&maximum)) / 2; + bounds->right = bounds->left + rect_width(&maximum); + bounds->bottom = bounds->top + rect_height(&maximum); +} + + + +//============================================================ +// update_minmax_state +// (window thread) +//============================================================ + +static void update_minmax_state(win_window_info *window) +{ + assert(GetCurrentThreadId() == window_threadid); + + if (!window->fullscreen) + { + RECT bounds, minbounds, maxbounds; + + // compare the maximum bounds versus the current bounds + get_min_bounds(window, &minbounds, video_config.keepaspect); + get_max_bounds(window, &maxbounds, video_config.keepaspect); + GetWindowRect(window->hwnd, &bounds); + + // if either the width or height matches, we were maximized + window->isminimized = (rect_width(&bounds) == rect_width(&minbounds) || + rect_height(&bounds) == rect_height(&minbounds)); + window->ismaximized = (rect_width(&bounds) == rect_width(&maxbounds) || + rect_height(&bounds) == rect_height(&maxbounds)); + } + else + { + window->isminimized = FALSE; + window->ismaximized = TRUE; + } +} + + + +//============================================================ +// minimize_window +// (window thread) +//============================================================ + +static void minimize_window(win_window_info *window) +{ + RECT newsize; + + assert(GetCurrentThreadId() == window_threadid); + + get_min_bounds(window, &newsize, video_config.keepaspect); + SetWindowPos(window->hwnd, NULL, newsize.left, newsize.top, rect_width(&newsize), rect_height(&newsize), SWP_NOZORDER); +} + + + +//============================================================ +// maximize_window +// (window thread) +//============================================================ + +static void maximize_window(win_window_info *window) +{ + RECT newsize; + + assert(GetCurrentThreadId() == window_threadid); + + get_max_bounds(window, &newsize, video_config.keepaspect); + SetWindowPos(window->hwnd, NULL, newsize.left, newsize.top, rect_width(&newsize), rect_height(&newsize), SWP_NOZORDER); +} + + + +//============================================================ +// adjust_window_position_after_major_change +// (window thread) +//============================================================ + +static void adjust_window_position_after_major_change(win_window_info *window) +{ + RECT oldrect, newrect; + + assert(GetCurrentThreadId() == window_threadid); + + // get the current size + GetWindowRect(window->hwnd, &oldrect); + + // adjust the window size so the client area is what we want + if (!window->fullscreen) + { + // constrain the existing size to the aspect ratio + newrect = oldrect; + if (video_config.keepaspect) + constrain_to_aspect_ratio(window, &newrect, WMSZ_BOTTOMRIGHT); + } + + // in full screen, make sure it covers the primary display + else + { + win_monitor_info *monitor = winwindow_video_window_monitor(window, NULL); + newrect = monitor->info.rcMonitor; + } + + // adjust the position if different + if (oldrect.left != newrect.left || oldrect.top != newrect.top || + oldrect.right != newrect.right || oldrect.bottom != newrect.bottom) + SetWindowPos(window->hwnd, window->fullscreen ? HWND_TOPMOST : HWND_TOP, + newrect.left, newrect.top, + rect_width(&newrect), rect_height(&newrect), 0); + + // take note of physical window size (used for lightgun coordinate calculation) + if (window == win_window_list) + { + win_physical_width = rect_width(&newrect); + win_physical_height = rect_height(&newrect); + logerror("Physical width %d, height %d\n",win_physical_width,win_physical_height); + } +} + + + +//============================================================ +// set_fullscreen +// (window thread) +//============================================================ + +static void set_fullscreen(win_window_info *window, int fullscreen) +{ + assert(GetCurrentThreadId() == window_threadid); + + // if we're in the right state, punt + if (window->fullscreen == fullscreen) + return; + window->fullscreen = fullscreen; + + // kill off the drawers + (*draw.window_destroy)(window); + + // hide ourself + ShowWindow(window->hwnd, SW_HIDE); + + // configure the window if non-fullscreen + if (!fullscreen) + { + // adjust the style + SetWindowLong(window->hwnd, GWL_STYLE, WINDOW_STYLE); + SetWindowLong(window->hwnd, GWL_EXSTYLE, WINDOW_STYLE_EX); + SetWindowPos(window->hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + + // force to the bottom, then back on top + SetWindowPos(window->hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + SetWindowPos(window->hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + + // if we have previous non-fullscreen bounds, use those + if (window->non_fullscreen_bounds.right != window->non_fullscreen_bounds.left) + { + SetWindowPos(window->hwnd, HWND_TOP, window->non_fullscreen_bounds.left, window->non_fullscreen_bounds.top, + rect_width(&window->non_fullscreen_bounds), rect_height(&window->non_fullscreen_bounds), + SWP_NOZORDER); + } + + // otherwise, set a small size and maximize from there + else + { + SetWindowPos(window->hwnd, HWND_TOP, 0, 0, MIN_WINDOW_DIM, MIN_WINDOW_DIM, SWP_NOZORDER); + maximize_window(window); + } + } + + // configure the window if fullscreen + else + { + // save the bounds + GetWindowRect(window->hwnd, &window->non_fullscreen_bounds); + + // adjust the style + SetWindowLong(window->hwnd, GWL_STYLE, FULLSCREEN_STYLE); + SetWindowLong(window->hwnd, GWL_EXSTYLE, FULLSCREEN_STYLE_EX); + SetWindowPos(window->hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + + // set topmost + SetWindowPos(window->hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + + // adjust the window to compensate for the change + adjust_window_position_after_major_change(window); + + // show ourself + if (!window->fullscreen || window->fullscreen_safe) + { + if (video_config.mode != VIDEO_MODE_NONE) + ShowWindow(window->hwnd, SW_SHOW); + if ((*draw.window_init)(window)) + exit(1); + } + + // ensure we're still adjusted correctly + adjust_window_position_after_major_change(window); +} diff --git a/src/osd/windows/window.h b/src/osd/windows/window.h new file mode 100644 index 00000000000..31684704e0c --- /dev/null +++ b/src/osd/windows/window.h @@ -0,0 +1,160 @@ +//============================================================ +// +// window.h - Win32 window handling +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __WIN_WINDOW__ +#define __WIN_WINDOW__ + +#include "video.h" +#include "render.h" + + +//============================================================ +// PARAMETERS +//============================================================ + +#ifndef MESS +#define HAS_WINDOW_MENU FALSE +#else +#define HAS_WINDOW_MENU TRUE +#endif + + + +//============================================================ +// CONSTANTS +//============================================================ + +#define RESIZE_STATE_NORMAL 0 +#define RESIZE_STATE_RESIZING 1 +#define RESIZE_STATE_PENDING 2 + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef struct _win_window_info win_window_info; +struct _win_window_info +{ + win_window_info * next; + volatile int init_state; + + // window handle and info + HWND hwnd; + char title[256]; + RECT non_fullscreen_bounds; + int startmaximized; + int isminimized; + int ismaximized; + int resize_state; + + // monitor info + win_monitor_info * monitor; + int fullscreen; + int fullscreen_safe; + int maxwidth, maxheight; + int refresh; + float aspect; + + // rendering info + osd_lock * render_lock; + render_target * target; + int targetview; + int targetorient; + int targetlayerconfig; + const render_primitive_list *primlist; + + // drawing data + void * drawdata; +}; + + +typedef struct _win_draw_callbacks win_draw_callbacks; +struct _win_draw_callbacks +{ + void (*exit)(void); + + int (*window_init)(win_window_info *window); + const render_primitive_list *(*window_get_primitives)(win_window_info *window); + int (*window_draw)(win_window_info *window, HDC dc, int update); + void (*window_destroy)(win_window_info *window); +}; + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +// windows +extern win_window_info *win_window_list; + + + +//============================================================ +// PROTOTYPES +//============================================================ + +// core initialization +void winwindow_init(running_machine *machine); + +// creation/deletion of windows +void winwindow_video_window_create(int index, win_monitor_info *monitor, const win_window_config *config); + +BOOL winwindow_has_focus(void); +void winwindow_update_cursor_state(void); +void winwindow_video_window_update(win_window_info *window); +win_monitor_info *winwindow_video_window_monitor(win_window_info *window, const RECT *proposed); + +LRESULT CALLBACK winwindow_video_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam); +void winwindow_toggle_full_screen(void); + +void winwindow_process_events_periodic(void); +void winwindow_process_events(int ingame); + +void winwindow_ui_pause_from_window_thread(int pause); +void winwindow_ui_pause_from_main_thread(int pause); +int winwindow_ui_is_paused(void); + +void winwindow_ui_exec_on_main_thread(void (*func)(void *), void *param); +void winwindow_dispatch_message(MSG *message); + +#if HAS_WINDOW_MENU +int win_create_menu(HMENU *menus); +#endif + + + +//============================================================ +// win_has_menu +//============================================================ + +INLINE BOOL win_has_menu(win_window_info *window) +{ + return GetMenu(window->hwnd) ? TRUE : FALSE; +} + + +//============================================================ +// rect_width / rect_height +//============================================================ + +INLINE int rect_width(const RECT *rect) +{ + return rect->right - rect->left; +} + + +INLINE int rect_height(const RECT *rect) +{ + return rect->bottom - rect->top; +} + +#endif diff --git a/src/osd/windows/windows.mak b/src/osd/windows/windows.mak new file mode 100644 index 00000000000..be5bc9c4948 --- /dev/null +++ b/src/osd/windows/windows.mak @@ -0,0 +1,299 @@ +########################################################################### +# +# windows.mak +# +# Windows-specific makefile +# +# Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +# Visit http://mamedev.org for licensing and usage restrictions. +# +########################################################################### + + +########################################################################### +################# BEGIN USER-CONFIGURABLE OPTIONS ##################### +########################################################################### + + +#------------------------------------------------- +# specify build options; see each option below +# for details +#------------------------------------------------- + +# uncomment next line to enable a build using Microsoft tools +# MSVC_BUILD = 1 + +# uncomment next line to use cygwin compiler +# CYGWIN_BUILD = 1 + +# uncomment next line to enable multi-monitor stubs on Windows 95/NT +# you will need to find multimon.h and put it into your include +# path in order to make this work +# WIN95_MULTIMON = 1 + +# uncomment next line to enable a Unicode build +# UNICODE = 1 + + + +########################################################################### +################## END USER-CONFIGURABLE OPTIONS ###################### +########################################################################### + + +#------------------------------------------------- +# object and source roots +#------------------------------------------------- + +WINSRC = $(SRC)/osd/$(OSD) +WINOBJ = $(OBJ)/osd/$(OSD) + +OBJDIRS += $(WINOBJ) + + + +#------------------------------------------------- +# configure the resource compiler +#------------------------------------------------- + +RC = @windres --use-temp-file + +RCDEFS = -DNDEBUG -D_WIN32_IE=0x0400 + +RCFLAGS = -O coff -I $(WINSRC) -I $(WINOBJ) + + + +#------------------------------------------------- +# overrides for the CYGWIN compiler +#------------------------------------------------- + +ifdef CYGWIN_BUILD +CFLAGS += -mno-cygwin +LDFLAGS += -mno-cygwin +endif + + + +#------------------------------------------------- +# overrides for the MSVC compiler +#------------------------------------------------- + +ifdef MSVC_BUILD + +VCONV = $(WINOBJ)/vconv$(EXE) + +# replace the various compilers with vconv.exe prefixes +CC = @$(VCONV) gcc -I. +LD = @$(VCONV) ld /profile +AR = @$(VCONV) ar +RC = @$(VCONV) windres + +# make sure we use the multithreaded runtime +ifdef DEBUG +CC += /MTd +else +CC += /MT +endif + +# turn on link-time codegen if the MAXOPT flag is also set +ifdef MAXOPT +CC += /GL +LD += /LTCG +endif + +ifdef PTR64 +CC += /wd4267 /Wp64 +endif + +# add some VC++-specific defines +DEFS += -D_CRT_SECURE_NO_DEPRECATE -DXML_STATIC -D__inline__=__inline -Dsnprintf=_snprintf -Dvsnprintf=_vsnprintf + +# make msvcprep into a pre-build step +# OSPREBUILD = $(VCONV) + +# add VCONV to the build tools +BUILD += $(VCONV) + +$(VCONV): $(WINOBJ)/vconv.o + @echo Linking $@... +ifdef PTR64 + @link.exe /nologo $^ version.lib bufferoverflowu.lib /out:$@ +else + @link.exe /nologo $^ version.lib /out:$@ +endif + +$(WINOBJ)/vconv.o: $(WINSRC)/vconv.c + @echo Compiling $<... + @cl.exe /nologo /O1 -D_CRT_SECURE_NO_DEPRECATE -c $< /Fo$@ + +endif + + + +#------------------------------------------------- +# due to quirks of using /bin/sh, we need to +# explicitly specify the current path +#------------------------------------------------- + +CURPATH = ./ + + + +#------------------------------------------------- +# Windows-specific debug objects and flags +#------------------------------------------------- + +# define the x64 ABI to be Windows +DEFS += -DX64_WINDOWS_ABI + +# map all instances of "main" to "utf8_main" +DEFS += -Dmain=utf8_main + +# debug build: enable guard pages on all memory allocations +ifdef DEBUG +ifndef WINUI +DEFS += -DMALLOC_DEBUG +LDFLAGS += -Wl,--allow-multiple-definition +endif +endif + +# enable UNICODE flags for unicode builds +ifdef UNICODE +DEFS += -DUNICODE -D_UNICODE +endif + + + +#------------------------------------------------- +# Windows-specific flags and libraries +#------------------------------------------------- + +# add our prefix files to the mix +CFLAGS += -include $(WINSRC)/winprefix.h + +ifdef WIN95_MULTIMON +CFLAGS += -DWIN95_MULTIMON +endif + +# add the windows libaries +LIBS += -luser32 -lgdi32 -lddraw -ldsound -ldinput -ldxguid -lwinmm -ladvapi32 -lcomctl32 -lshlwapi + +ifdef PTR64 +LIBS += -lbufferoverflowu +endif + + + +#------------------------------------------------- +# OSD core library +#------------------------------------------------- + +OSDCOREOBJS = \ + $(WINOBJ)/main.o \ + $(WINOBJ)/strconv.o \ + $(WINOBJ)/windir.o \ + $(WINOBJ)/winfile.o \ + $(WINOBJ)/winmisc.o \ + $(WINOBJ)/winsync.o \ + $(WINOBJ)/wintime.o \ + $(WINOBJ)/winutf8.o \ + $(WINOBJ)/winutil.o \ + $(WINOBJ)/winwork.o \ + +# if malloc debugging is enabled, include the necessary code +ifneq ($(findstring MALLOC_DEBUG,$(DEFS)),) +OSDCOREOBJS += \ + $(WINOBJ)/winalloc.o +endif + + + +#------------------------------------------------- +# OSD Windows library +#------------------------------------------------- + +OSDOBJS = \ + $(WINOBJ)/d3d8intf.o \ + $(WINOBJ)/d3d9intf.o \ + $(WINOBJ)/drawd3d.o \ + $(WINOBJ)/drawdd.o \ + $(WINOBJ)/drawgdi.o \ + $(WINOBJ)/drawnone.o \ + $(WINOBJ)/input.o \ + $(WINOBJ)/output.o \ + $(WINOBJ)/sound.o \ + $(WINOBJ)/video.o \ + $(WINOBJ)/window.o \ + $(WINOBJ)/winmain.o + +# extra dependencies +$(WINOBJ)/drawdd.o : $(SRC)/emu/rendersw.c +$(WINOBJ)/drawgdi.o : $(SRC)/emu/rendersw.c + +# add debug-specific files +ifdef DEBUG +OSDOBJS += \ + $(WINOBJ)/debugwin.o +endif + +# add a stub resource file +RESFILE = $(WINOBJ)/mame.res + + + +#------------------------------------------------- +# rules for building the libaries +#------------------------------------------------- + +$(LIBOCORE): $(OSDCOREOBJS) + +$(LIBOSD): $(OSDOBJS) + + + +#------------------------------------------------- +# rule for making the ledutil sample +#------------------------------------------------- + +ledutil$(EXE): $(WINOBJ)/ledutil.o $(LIBOCORE) + @echo Linking $@... + $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ + +TOOLS += ledutil$(EXE) + + + +#------------------------------------------------- +# rule for making the verinfo tool +#------------------------------------------------- + +VERINFO = $(WINOBJ)/verinfo$(EXE) + +$(VERINFO): $(WINOBJ)/verinfo.o $(LIBOCORE) + @echo Linking $@... + $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ + +BUILD += $(VERINFO) + + + +#------------------------------------------------- +# generic rule for the resource compiler +#------------------------------------------------- + +$(WINOBJ)/%.res: $(WINSRC)/%.rc | $(OSPREBUILD) + @echo Compiling resources $<... + $(RC) $(RCDEFS) $(RCFLAGS) -o $@ -i $< + + + +#------------------------------------------------- +# rules for resource file +#------------------------------------------------- + +$(RESFILE): $(WINSRC)/mame.rc $(WINOBJ)/mamevers.rc + +$(WINOBJ)/mamevers.rc: $(VERINFO) $(SRC)/version.c + @echo Emitting $@... + @$(VERINFO) $(SRC)/version.c > $@ diff --git a/src/osd/windows/winfile.c b/src/osd/windows/winfile.c new file mode 100644 index 00000000000..6238e572cb2 --- /dev/null +++ b/src/osd/windows/winfile.c @@ -0,0 +1,331 @@ +//============================================================ +// +// winfile.c - Win32 OSD core file access functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +// MAME headers +#include "osdcore.h" + +// MAMEOS headers +#include "strconv.h" +#include "winutil.h" + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +struct _osd_file +{ + HANDLE handle; + TCHAR filename[1]; +}; + + + +//============================================================ +// FUNCTION PROTOTYPES +//============================================================ + +static DWORD create_path_recursive(const TCHAR *path); + + + +//============================================================ +// osd_open +//============================================================ + +file_error osd_open(const char *path, UINT32 openflags, osd_file **file, UINT64 *filesize) +{ + DWORD disposition, access, sharemode; + file_error filerr = FILERR_NONE; + const TCHAR *src; + DWORD upper; + TCHAR *t_path; + TCHAR *dst; + + // convert path to TCHAR + t_path = tstring_from_utf8(path); + if (t_path == NULL) + { + filerr = FILERR_OUT_OF_MEMORY; + goto error; + } + + // allocate a file object, plus space for the converted filename + *file = malloc(sizeof(**file) + sizeof(TCHAR) * _tcslen(t_path)); + if (*file == NULL) + { + filerr = FILERR_OUT_OF_MEMORY; + goto error; + } + + // convert the path into something Windows compatible + dst = (*file)->filename; + for (src = t_path; *src != 0; src++) + *dst++ = *src;//(*src == '/') ? '\\' : *src; + *dst++ = 0; + + // select the file open modes + if (openflags & OPEN_FLAG_WRITE) + { + disposition = (openflags & OPEN_FLAG_CREATE) ? CREATE_ALWAYS : OPEN_EXISTING; + access = (openflags & OPEN_FLAG_READ) ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_WRITE; + sharemode = 0; + } + else if (openflags & OPEN_FLAG_READ) + { + disposition = OPEN_EXISTING; + access = GENERIC_READ; + sharemode = FILE_SHARE_READ; + } + else + { + filerr = FILERR_INVALID_ACCESS; + goto error; + } + + // attempt to open the file + (*file)->handle = CreateFile((*file)->filename, access, sharemode, NULL, disposition, 0, NULL); + if ((*file)->handle == INVALID_HANDLE_VALUE) + { + DWORD error = GetLastError(); + + // create the path if necessary + if (error == ERROR_PATH_NOT_FOUND && (openflags & OPEN_FLAG_CREATE) && (openflags & OPEN_FLAG_CREATE_PATHS)) + { + TCHAR *pathsep = _tcsrchr((*file)->filename, '\\'); + if (pathsep != NULL) + { + // create the path up to the file + *pathsep = 0; + error = create_path_recursive((*file)->filename); + *pathsep = '\\'; + + // attempt to reopen the file + if (error == NO_ERROR) + (*file)->handle = CreateFile((*file)->filename, access, sharemode, NULL, disposition, 0, NULL); + } + } + + // if we still failed, clean up and free + if ((*file)->handle == INVALID_HANDLE_VALUE) + { + filerr = win_error_to_file_error(error); + goto error; + } + } + + // get the file size + *filesize = GetFileSize((*file)->handle, &upper); + *filesize |= (UINT64)upper << 32; + +error: + // cleanup + if (filerr != FILERR_NONE && *file != NULL) + { + free(*file); + *file = NULL; + } + free(t_path); + return filerr; +} + + +//============================================================ +// osd_read +//============================================================ + +file_error osd_read(osd_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual) +{ + LONG upper = offset >> 32; + DWORD result; + + // attempt to set the file pointer + result = SetFilePointer(file->handle, (UINT32)offset, &upper, FILE_BEGIN); + if (result == INVALID_SET_FILE_POINTER) + { + DWORD error = GetLastError(); + if (error != NO_ERROR) + return win_error_to_file_error(error); + } + + // then perform the read + if (!ReadFile(file->handle, buffer, length, &result, NULL)) + return win_error_to_file_error(GetLastError()); + if (actual != NULL) + *actual = result; + return FILERR_NONE; +} + + +//============================================================ +// osd_write +//============================================================ + +file_error osd_write(osd_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual) +{ + LONG upper = offset >> 32; + DWORD result; + + // attempt to set the file pointer + result = SetFilePointer(file->handle, (UINT32)offset, &upper, FILE_BEGIN); + if (result == INVALID_SET_FILE_POINTER) + { + DWORD error = GetLastError(); + if (error != NO_ERROR) + return win_error_to_file_error(error); + } + + // then perform the read + if (!WriteFile(file->handle, buffer, length, &result, NULL)) + return win_error_to_file_error(GetLastError()); + if (actual != NULL) + *actual = result; + return FILERR_NONE; +} + + +//============================================================ +// osd_close +//============================================================ + +file_error osd_close(osd_file *file) +{ + // close the file handle and free the file structure + CloseHandle(file->handle); + free(file); + return FILERR_NONE; +} + + +//============================================================ +// osd_rmfile +//============================================================ + +file_error osd_rmfile(const char *filename) +{ + file_error filerr = FILERR_NONE; + + TCHAR *tempstr = tstring_from_utf8(filename); + if (!tempstr) + { + filerr = FILERR_OUT_OF_MEMORY; + goto done; + } + + if (!DeleteFile(tempstr)) + { + filerr = win_error_to_file_error(GetLastError()); + goto done; + } + +done: + if (tempstr) + free(tempstr); + return filerr; +} + + +//============================================================ +// osd_get_physical_drive_geometry +//============================================================ + +int osd_get_physical_drive_geometry(const char *filename, UINT32 *cylinders, UINT32 *heads, UINT32 *sectors, UINT32 *bps) +{ + DISK_GEOMETRY dg; + DWORD bytesRead; + TCHAR *t_filename; + HANDLE file; + int result; + + // if it doesn't smell like a physical drive, just return FALSE + if (_strnicmp(filename, "\\\\.\\physicaldrive", 17) != 0) + return FALSE; + + // do a create file on the drive + t_filename = tstring_from_utf8(filename); + if (t_filename == NULL) + return FALSE; + file = CreateFile(t_filename, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL); + free(t_filename); + if (file == INVALID_HANDLE_VALUE) + return FALSE; + + // device I/O control should return the geometry + result = DeviceIoControl(file, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dg, sizeof(dg), &bytesRead, NULL); + CloseHandle(file); + + // if that failed, return false + if (!result) + return FALSE; + + // store the results + *cylinders = (UINT32)dg.Cylinders.QuadPart; + *heads = dg.TracksPerCylinder; + *sectors = dg.SectorsPerTrack; + *bps = dg.BytesPerSector; + + // normalize + while (*heads > 16 && !(*heads & 1)) + { + *heads /= 2; + *cylinders *= 2; + } + return TRUE; +} + + +//============================================================ +// osd_uchar_from_osdchar +//============================================================ + +int osd_uchar_from_osdchar(UINT32 *uchar, const char *osdchar, size_t count) +{ + WCHAR wch; + + count = MIN(count, IsDBCSLeadByte(*osdchar) ? 2 : 1); + if (MultiByteToWideChar(CP_ACP, 0, osdchar, (DWORD)count, &wch, 1) != 0) + *uchar = wch; + else + *uchar = 0; + return (int) count; +} + + +//============================================================ +// create_path_recursive +//============================================================ + +DWORD create_path_recursive(const TCHAR *path) +{ + TCHAR *sep = _tcsrchr(path, '\\'); + file_error filerr; + + // if there's still a separator, and it's not the root, nuke it and recurse + if (sep != NULL && sep > path && sep[0] != ':' && sep[-1] != '\\') + { + *sep = 0; + filerr = create_path_recursive(path); + *sep = '\\'; + } + + // if the path already exists, we're done + if (GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES) + return NO_ERROR; + + // create the path + if (CreateDirectory(path, NULL) == 0) + return GetLastError(); + return NO_ERROR; +} diff --git a/src/osd/windows/winmain.c b/src/osd/windows/winmain.c new file mode 100644 index 00000000000..2879f328d3f --- /dev/null +++ b/src/osd/windows/winmain.c @@ -0,0 +1,844 @@ +//============================================================ +// +// winmain.c - Win32 main program +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +// standard C headers +#include +#include + +// MAME headers +#include "driver.h" +#include "clifront.h" + +// MAMEOS headers +#include "winmain.h" +#include "window.h" +#include "video.h" +#include "sound.h" +#include "input.h" +#include "output.h" +#include "config.h" +#include "osdepend.h" +#include "strconv.h" +#include "winutf8.h" +#include "winutil.h" + +#define ENABLE_PROFILER 0 +#define DEBUG_SLOW_LOCKS 0 + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +#ifdef UNICODE +#define UNICODE_POSTFIX "W" +#else +#define UNICODE_POSTFIX "A" +#endif + +typedef BOOL (WINAPI *try_enter_critical_section_ptr)(LPCRITICAL_SECTION lpCriticalSection); + +typedef HANDLE (WINAPI *av_set_mm_thread_characteristics_ptr)(LPCTSTR TaskName, LPDWORD TaskIndex); +typedef HANDLE (WINAPI *av_set_mm_max_thread_characteristics_ptr)(LPCTSTR FirstTask, LPCTSTR SecondTask, LPDWORD TaskIndex); +typedef BOOL (WINAPI *av_revert_mm_thread_characteristics_ptr)(HANDLE AvrtHandle); + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +// this line prevents globbing on the command line +int _CRT_glob = 0; + + + +//============================================================ +// LOCAL VARIABLES +//============================================================ + +static try_enter_critical_section_ptr try_enter_critical_section; + +static av_set_mm_thread_characteristics_ptr av_set_mm_thread_characteristics; +static av_set_mm_max_thread_characteristics_ptr av_set_mm_max_thread_characteristics; +static av_revert_mm_thread_characteristics_ptr av_revert_mm_thread_characteristics; + +static char mapfile_name[MAX_PATH]; +static LPTOP_LEVEL_EXCEPTION_FILTER pass_thru_filter; + +#ifndef MESS +static const TCHAR helpfile[] = TEXT("docs\\windows.txt"); +#else +static const TCHAR helpfile[] = TEXT("mess.chm"); +#endif + +//static HANDLE mm_task = NULL; +//static DWORD task_index = 0; +static int timeresult; +//static MMRESULT result; +static TIMECAPS caps; + + + +//============================================================ +// PROTOTYPES +//============================================================ + +static void osd_exit(running_machine *machine); + +static void soft_link_functions(void); +static int is_double_click_start(int argc); +static LONG CALLBACK exception_filter(struct _EXCEPTION_POINTERS *info); +static const char *lookup_symbol(UINT32 address); +static int get_code_base_size(UINT32 *base, UINT32 *size); + +static void start_profiler(void); +static void stop_profiler(void); + + + +//============================================================ +// OPTIONS +//============================================================ + +// struct definitions +const options_entry mame_win_options[] = +{ + // debugging options + { NULL, NULL, OPTION_HEADER, "WINDOWS DEBUGGING OPTIONS" }, + { "oslog", "0", OPTION_BOOLEAN, "output error.log data to the system debugger" }, + + // performance options + { NULL, NULL, OPTION_HEADER, "WINDOWS PERFORMANCE OPTIONS" }, + { "priority(-15-1)", "0", 0, "thread priority for the main game thread; range from -15 to 1" }, + { "multithreading;mt", "0", OPTION_BOOLEAN, "enable multithreading; this enables rendering and blitting on a separate thread" }, + + // video options + { NULL, NULL, OPTION_HEADER, "WINDOWS VIDEO OPTIONS" }, + { "video", "d3d", 0, "video output method: none, gdi, ddraw, or d3d" }, + { "numscreens(1-4)", "1", 0, "number of screens to create; usually, you want just one" }, + { "window;w", "0", OPTION_BOOLEAN, "enable window mode; otherwise, full screen mode is assumed" }, + { "maximize;max", "1", OPTION_BOOLEAN, "default to maximized windows; otherwise, windows will be minimized" }, + { "keepaspect;ka", "1", OPTION_BOOLEAN, "constrain to the proper aspect ratio" }, + { "prescale", "1", 0, "scale screen rendering by this amount in software" }, + { "effect", "none", 0, "name of a PNG file to use for visual effects, or 'none'" }, + { "waitvsync", "0", OPTION_BOOLEAN, "enable waiting for the start of VBLANK before flipping screens; reduces tearing effects" }, + { "syncrefresh", "0", OPTION_BOOLEAN, "enable using the start of VBLANK for throttling instead of the game time" }, + + // DirectDraw-specific options + { NULL, NULL, OPTION_HEADER, "DIRECTDRAW-SPECIFIC OPTIONS" }, + { "hwstretch;hws", "1", OPTION_BOOLEAN, "enable hardware stretching" }, + + // Direct3D-specific options + { NULL, NULL, OPTION_HEADER, "DIRECT3D-SPECIFIC OPTIONS" }, + { "d3dversion(8-9)", "9", 0, "specify the preferred Direct3D version (8 or 9)" }, + { "filter;d3dfilter;flt", "1", OPTION_BOOLEAN, "enable bilinear filtering on screen output" }, + + // per-window options + { NULL, NULL, OPTION_HEADER, "PER-WINDOW VIDEO OPTIONS" }, + { "screen", "auto", 0, "explicit name of all screens; 'auto' here will try to make a best guess" }, + { "aspect;screen_aspect", "auto", 0, "aspect ratio for all screens; 'auto' here will try to make a best guess" }, + { "resolution;r", "auto", 0, "preferred resolution for all screens; format is x[@] or 'auto'" }, + { "view", "auto", 0, "preferred view for all screens" }, + + { "screen0", "auto", 0, "explicit name of the first screen; 'auto' here will try to make a best guess" }, + { "aspect0", "auto", 0, "aspect ratio of the first screen; 'auto' here will try to make a best guess" }, + { "resolution0;r0", "auto", 0, "preferred resolution of the first screen; format is x[@] or 'auto'" }, + { "view0", "auto", 0, "preferred view for the first screen" }, + + { "screen1", "auto", 0, "explicit name of the second screen; 'auto' here will try to make a best guess" }, + { "aspect1", "auto", 0, "aspect ratio of the second screen; 'auto' here will try to make a best guess" }, + { "resolution1;r1", "auto", 0, "preferred resolution of the second screen; format is x[@] or 'auto'" }, + { "view1", "auto", 0, "preferred view for the second screen" }, + + { "screen2", "auto", 0, "explicit name of the third screen; 'auto' here will try to make a best guess" }, + { "aspect2", "auto", 0, "aspect ratio of the third screen; 'auto' here will try to make a best guess" }, + { "resolution2;r2", "auto", 0, "preferred resolution of the third screen; format is x[@] or 'auto'" }, + { "view2", "auto", 0, "preferred view for the third screen" }, + + { "screen3", "auto", 0, "explicit name of the fourth screen; 'auto' here will try to make a best guess" }, + { "aspect3", "auto", 0, "aspect ratio of the fourth screen; 'auto' here will try to make a best guess" }, + { "resolution3;r3", "auto", 0, "preferred resolution of the fourth screen; format is x[@] or 'auto'" }, + { "view3", "auto", 0, "preferred view for the fourth screen" }, + + // full screen options + { NULL, NULL, OPTION_HEADER, "FULL SCREEN OPTIONS" }, + { "triplebuffer;tb", "0", OPTION_BOOLEAN, "enable triple buffering" }, + { "switchres", "0", OPTION_BOOLEAN, "enable resolution switching" }, + { "full_screen_brightness;fsb(0.1-2.0)","1.0", 0, "brightness value in full screen mode" }, + { "full_screen_contrast;fsc(0.1-2.0)", "1.0", 0, "contrast value in full screen mode" }, + { "full_screen_gamma;fsg(0.1-3.0)", "1.0", 0, "gamma value in full screen mode" }, + + // sound options + { NULL, NULL, OPTION_HEADER, "WINDOWS SOUND OPTIONS" }, + { "audio_latency(1-5)", "2", 0, "set audio latency (increase to reduce glitches)" }, + + // input options + { NULL, NULL, OPTION_HEADER, "INPUT DEVICE OPTIONS" }, + { "dual_lightgun;dual", "0", OPTION_BOOLEAN, "enable dual lightgun input" }, + + { NULL } +}; + + +//============================================================ +// winui_output_error +//============================================================ + +static void winui_output_error(void *param, const char *format, va_list argptr) +{ + char buffer[1024]; + + // if we are in fullscreen mode, go to windowed mode + if ((video_config.windowed == 0) && (win_window_list != NULL)) + winwindow_toggle_full_screen(); + + vsnprintf(buffer, ARRAY_LENGTH(buffer), format, argptr); + win_message_box_utf8(win_window_list ? win_window_list->hwnd : NULL, buffer, APPNAME, MB_OK); +} + + + +//============================================================ +// utf8_main +//============================================================ + +int utf8_main(int argc, char **argv) +{ + char *ext; + + // initialize common controls + InitCommonControls(); + + // set up exception handling + pass_thru_filter = SetUnhandledExceptionFilter(exception_filter); + + // if we're a GUI app, out errors to message boxes + if (win_is_gui_application() || is_double_click_start(argc)) + { + // if we are a GUI app, output errors to message boxes + mame_set_output_channel(OUTPUT_CHANNEL_ERROR, winui_output_error, NULL, NULL, NULL); + + // make sure any console window that opened on our behalf is nuked + FreeConsole(); + } + + // soft-link optional functions + soft_link_functions(); + + strcpy(mapfile_name, argv[0]); + ext = strchr(mapfile_name, '.'); + if (ext) + strcpy(ext, ".map"); + else + strcat(mapfile_name, ".map"); + + // parse config and cmdline options + return cli_execute(argc, argv, mame_win_options); +} + + +//============================================================ +// output_oslog +//============================================================ + +static void output_oslog(running_machine *machine, const char *buffer) +{ + win_output_debug_string_utf8(buffer); +} + + +//============================================================ +// osd_init +//============================================================ + +void osd_init(running_machine *machine) +{ + // thread priority + if (!options_get_bool(mame_options(), OPTION_DEBUG)) + SetThreadPriority(GetCurrentThread(), options_get_int(mame_options(), WINOPTION_PRIORITY)); + + // ensure we get called on the way out + add_exit_callback(machine, osd_exit); + + // initialize the subsystems + winvideo_init(machine); + winsound_init(machine); + wininput_init(machine); + winoutput_init(machine); + + // hook up the debugger log + if (options_get_bool(mame_options(), WINOPTION_OSLOG)) + add_logerror_callback(machine, output_oslog); + + // crank up the multimedia timer resolution to its max + // this gives the system much finer timeslices + timeresult = timeGetDevCaps(&caps, sizeof(caps)); + if (timeresult == TIMERR_NOERROR) + timeBeginPeriod(caps.wPeriodMin); + + // set our multimedia tasks if we can +// if (av_set_mm_thread_characteristics != NULL) +// mm_task = (*av_set_mm_thread_characteristics)(TEXT("Playback"), &task_index); + + start_profiler(); +} + + +static void osd_exit(running_machine *machine) +{ + stop_profiler(); + + // turn off our multimedia tasks +// if (av_revert_mm_thread_characteristics != NULL) +// (*av_revert_mm_thread_characteristics)(mm_task); + + // restore the timer resolution + if (timeresult == TIMERR_NOERROR) + timeEndPeriod(caps.wPeriodMin); + + // one last pass at events + winwindow_process_events(0); +} + + + +//============================================================ +// soft_link_functions +//============================================================ + +static void soft_link_functions(void) +{ + HMODULE library; + + // see if we can use TryEnterCriticalSection + try_enter_critical_section = NULL; + library = LoadLibrary(TEXT("kernel32.dll")); + if (library != NULL) + try_enter_critical_section = (try_enter_critical_section_ptr)GetProcAddress(library, "TryEnterCriticalSection"); + + // see if we can use the multimedia scheduling functions in Vista + av_set_mm_thread_characteristics = NULL; + av_set_mm_max_thread_characteristics = NULL; + av_revert_mm_thread_characteristics = NULL; + library = LoadLibrary(TEXT("avrt.dll")); + if (library != NULL) + { + av_set_mm_thread_characteristics = (av_set_mm_thread_characteristics_ptr)GetProcAddress(library, "AvSetMmThreadCharacteristics" UNICODE_POSTFIX); + av_set_mm_max_thread_characteristics = (av_set_mm_max_thread_characteristics_ptr)GetProcAddress(library, "AvSetMmMaxThreadCharacteristics" UNICODE_POSTFIX); + av_revert_mm_thread_characteristics = (av_revert_mm_thread_characteristics_ptr)GetProcAddress(library, "AvRevertMmThreadCharacteristics"); + } +} + + +//============================================================ +// check_for_double_click_start +//============================================================ + +static int is_double_click_start(int argc) +{ + STARTUPINFO startup_info = { sizeof(STARTUPINFO) }; + + // determine our startup information + GetStartupInfo(&startup_info); + + // try to determine if MAME was simply double-clicked + return (argc <= 1 && startup_info.dwFlags && !(startup_info.dwFlags & STARTF_USESTDHANDLES)); +} + + +//============================================================ +// exception_filter +//============================================================ + +static LONG CALLBACK exception_filter(struct _EXCEPTION_POINTERS *info) +{ + static const struct + { + DWORD code; + const char *string; + } exception_table[] = + { + { EXCEPTION_ACCESS_VIOLATION, "ACCESS VIOLATION" }, + { EXCEPTION_DATATYPE_MISALIGNMENT, "DATATYPE MISALIGNMENT" }, + { EXCEPTION_BREAKPOINT, "BREAKPOINT" }, + { EXCEPTION_SINGLE_STEP, "SINGLE STEP" }, + { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "ARRAY BOUNDS EXCEEDED" }, + { EXCEPTION_FLT_DENORMAL_OPERAND, "FLOAT DENORMAL OPERAND" }, + { EXCEPTION_FLT_DIVIDE_BY_ZERO, "FLOAT DIVIDE BY ZERO" }, + { EXCEPTION_FLT_INEXACT_RESULT, "FLOAT INEXACT RESULT" }, + { EXCEPTION_FLT_INVALID_OPERATION, "FLOAT INVALID OPERATION" }, + { EXCEPTION_FLT_OVERFLOW, "FLOAT OVERFLOW" }, + { EXCEPTION_FLT_STACK_CHECK, "FLOAT STACK CHECK" }, + { EXCEPTION_FLT_UNDERFLOW, "FLOAT UNDERFLOW" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "INTEGER DIVIDE BY ZERO" }, + { EXCEPTION_INT_OVERFLOW, "INTEGER OVERFLOW" }, + { EXCEPTION_PRIV_INSTRUCTION, "PRIVILEGED INSTRUCTION" }, + { EXCEPTION_IN_PAGE_ERROR, "IN PAGE ERROR" }, + { EXCEPTION_ILLEGAL_INSTRUCTION, "ILLEGAL INSTRUCTION" }, + { EXCEPTION_NONCONTINUABLE_EXCEPTION,"NONCONTINUABLE EXCEPTION" }, + { EXCEPTION_STACK_OVERFLOW, "STACK OVERFLOW" }, + { EXCEPTION_INVALID_DISPOSITION, "INVALID DISPOSITION" }, + { EXCEPTION_GUARD_PAGE, "GUARD PAGE VIOLATION" }, + { EXCEPTION_INVALID_HANDLE, "INVALID HANDLE" }, + { 0, "UNKNOWN EXCEPTION" } + }; + static int already_hit = 0; +#ifndef PTR64 + UINT32 code_start, code_size; +#endif + int i; + + // if we're hitting this recursively, just exit + if (already_hit) + ExitProcess(100); + already_hit = 1; + +#ifdef MAME_DEBUG +{ +extern void debug_flush_traces(void); +debug_flush_traces(); +} +#endif + + // find our man + for (i = 0; exception_table[i].code != 0; i++) + if (info->ExceptionRecord->ExceptionCode == exception_table[i].code) + break; + + // print the exception type and address + fprintf(stderr, "\n-----------------------------------------------------\n"); + fprintf(stderr, "Exception at EIP=%p%s: %s\n", info->ExceptionRecord->ExceptionAddress, + lookup_symbol((FPTR)info->ExceptionRecord->ExceptionAddress), exception_table[i].string); + + // for access violations, print more info + if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) + fprintf(stderr, "While attempting to %s memory at %08X\n", + info->ExceptionRecord->ExceptionInformation[0] ? "write" : "read", + (UINT32)info->ExceptionRecord->ExceptionInformation[1]); + + // print the state of the CPU + fprintf(stderr, "-----------------------------------------------------\n"); +#ifdef PTR64 + fprintf(stderr, "RAX=%p RBX=%p RCX=%p RDX=%p\n", + (void *)info->ContextRecord->Rax, + (void *)info->ContextRecord->Rbx, + (void *)info->ContextRecord->Rcx, + (void *)info->ContextRecord->Rdx); + fprintf(stderr, "RSI=%p RDI=%p RBP=%p RSP=%p\n", + (void *)info->ContextRecord->Rsi, + (void *)info->ContextRecord->Rdi, + (void *)info->ContextRecord->Rbp, + (void *)info->ContextRecord->Rsp); + fprintf(stderr, " R8=%p R9=%p R10=%p R11=%p\n", + (void *)info->ContextRecord->R8, + (void *)info->ContextRecord->R9, + (void *)info->ContextRecord->R10, + (void *)info->ContextRecord->R11); + fprintf(stderr, "R12=%p R13=%p R14=%p R15=%p\n", + (void *)info->ContextRecord->R12, + (void *)info->ContextRecord->R13, + (void *)info->ContextRecord->R14, + (void *)info->ContextRecord->R15); +#else + fprintf(stderr, "EAX=%p EBX=%p ECX=%p EDX=%p\n", + (void *)info->ContextRecord->Eax, + (void *)info->ContextRecord->Ebx, + (void *)info->ContextRecord->Ecx, + (void *)info->ContextRecord->Edx); + fprintf(stderr, "ESI=%p EDI=%p EBP=%p ESP=%p\n", + (void *)info->ContextRecord->Esi, + (void *)info->ContextRecord->Edi, + (void *)info->ContextRecord->Ebp, + (void *)info->ContextRecord->Esp); +#endif + +#ifndef PTR64 + // crawl the stack for a while + if (get_code_base_size(&code_start, &code_size)) + { + char prev_symbol[1024], curr_symbol[1024]; + UINT32 last_call = (UINT32)info->ExceptionRecord->ExceptionAddress; + UINT32 esp_start = info->ContextRecord->Esp; + UINT32 esp_end = (esp_start | 0xffff) + 1; + UINT32 esp; + + // reprint the actual exception address + fprintf(stderr, "-----------------------------------------------------\n"); + fprintf(stderr, "Stack crawl:\n"); + fprintf(stderr, "exception-> %08X%s\n", last_call, strcpy(prev_symbol, lookup_symbol(last_call))); + + // crawl the stack until we hit the next 64k boundary + for (esp = esp_start; esp < esp_end; esp += 4) + { + UINT32 stack_val = *(UINT32 *)esp; + + // if the value on the stack points within the code block, check it out + if (stack_val >= code_start && stack_val < code_start + code_size) + { + UINT8 *return_addr = (UINT8 *)stack_val; + UINT32 call_target = 0; + + // make sure the code that we think got us here is actually a CALL instruction + if (return_addr[-5] == 0xe8) + call_target = stack_val - 5 + *(INT32 *)&return_addr[-4]; + if ((return_addr[-2] == 0xff && (return_addr[-1] & 0x38) == 0x10) || + (return_addr[-3] == 0xff && (return_addr[-2] & 0x38) == 0x10) || + (return_addr[-4] == 0xff && (return_addr[-3] & 0x38) == 0x10) || + (return_addr[-5] == 0xff && (return_addr[-4] & 0x38) == 0x10) || + (return_addr[-6] == 0xff && (return_addr[-5] & 0x38) == 0x10) || + (return_addr[-7] == 0xff && (return_addr[-6] & 0x38) == 0x10)) + call_target = 1; + + // make sure it points somewhere a little before the last call + if (call_target == 1 || (call_target < last_call && call_target >= last_call - 0x1000)) + { + char *stop_compare = strchr(prev_symbol, '+'); + + // don't print duplicate hits in the same routine + strcpy(curr_symbol, lookup_symbol(stack_val)); + if (stop_compare == NULL || strncmp(curr_symbol, prev_symbol, stop_compare - prev_symbol)) + { + strcpy(prev_symbol, curr_symbol); + fprintf(stderr, " %08X: %08X%s\n", esp, stack_val, curr_symbol); + last_call = stack_val; + } + } + } + } + } +#endif + + // exit + ExitProcess(100); +} + + + +//============================================================ +// lookup_symbol +//============================================================ + +static const char *lookup_symbol(UINT32 address) +{ + static char buffer[1024]; + FILE * map = fopen(mapfile_name, "r"); + char symbol[1024], best_symbol[1024]; + UINT32 addr, best_addr = 0; + char line[1024]; + + // if no file, return nothing + if (map == NULL) + return ""; + + // reset the bests + *best_symbol = 0; + best_addr = 0; + + // parse the file, looking for map entries + while (fgets(line, sizeof(line) - 1, map)) + if (!strncmp(line, " 0x", 18)) + if (sscanf(line, " 0x%08x %s", &addr, symbol) == 2) + if (addr <= address && addr > best_addr) + { + best_addr = addr; + strcpy(best_symbol, symbol); + } + + // create the final result + if (address - best_addr > 0x10000) + return ""; + sprintf(buffer, " (%s+0x%04x)", best_symbol, address - best_addr); + return buffer; +} + + + +//============================================================ +// get_code_base_size +//============================================================ + +static int get_code_base_size(UINT32 *base, UINT32 *size) +{ + FILE * map = fopen(mapfile_name, "r"); + char line[1024]; + + // if no file, return nothing + if (map == NULL) + return 0; + + // parse the file, looking for .text entry + while (fgets(line, sizeof(line) - 1, map)) + if (!strncmp(line, ".text 0x", 18)) + if (sscanf(line, ".text 0x%08x 0x%x", base, size) == 2) + return 1; + + return 0; +} + + +#if ENABLE_PROFILER + +//============================================================ +// +// profiler.c - Sampling profiler +// +//============================================================ + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +#define MAX_SYMBOLS 65536 + +typedef struct _map_entry map_entry; + +struct _map_entry +{ + UINT32 start; + UINT32 end; + UINT32 hits; + char *name; +}; + +//============================================================ +// LOCAL VARIABLES +//============================================================ + +static map_entry symbol_map[MAX_SYMBOLS]; +static int map_entries; + +static HANDLE profiler_thread; +static DWORD profiler_thread_id; +static volatile UINT8 profiler_thread_exit; + +//============================================================ +// compare_base +// compare_hits -- qsort callbacks to sort on +//============================================================ + +static int CLIB_DECL compare_start(const void *item1, const void *item2) +{ + return ((const map_entry *)item1)->start - ((const map_entry *)item2)->start; +} + +static int CLIB_DECL compare_hits(const void *item1, const void *item2) +{ + return ((const map_entry *)item2)->hits - ((const map_entry *)item1)->hits; +} + + +//============================================================ +// add_symbol_map_entry +// parse_map_file +//============================================================ + +static void add_symbol_map_entry(UINT32 start, const char *name) +{ + if (map_entries == MAX_SYMBOLS) + fatalerror("Symbol table full"); + symbol_map[map_entries].start = start; + symbol_map[map_entries].name = core_strdup(name); + if (symbol_map[map_entries].name == NULL) + fatalerror("Out of memory"); + map_entries++; +} + +static void parse_map_file(void) +{ + int got_text = 0; + char line[1024]; + FILE *map; + int i; + + // open the map file + map = fopen(mapfile_name, "r"); + if (!map) + return; + + // parse out the various symbols into map entries + map_entries = 0; + while (fgets(line, sizeof(line) - 1, map)) + { + /* look for the code boundaries */ + if (!got_text && !strncmp(line, ".text 0x", 18)) + { + UINT32 base, size; + if (sscanf(line, ".text 0x%08x 0x%x", &base, &size) == 2) + { + add_symbol_map_entry(base, "Code start"); + add_symbol_map_entry(base+size, "Other"); + got_text = 1; + } + } + + /* look for symbols */ + else if (!strncmp(line, " 0x", 18)) + { + char symbol[1024]; + UINT32 addr; + if (sscanf(line, " 0x%08x %s", &addr, symbol) == 2) + { + add_symbol_map_entry(addr, symbol); + } + } + } + + /* add a symbol for end-of-memory */ + add_symbol_map_entry(~0, ""); + map_entries--; + + /* close the file */ + fclose(map); + + /* sort by address */ + qsort(symbol_map, map_entries, sizeof(symbol_map[0]), compare_start); + + /* fill in the end of each bucket */ + for (i = 0; i < map_entries; i++) + symbol_map[i].end = symbol_map[i+1].start ? (symbol_map[i+1].start - 1) : 0; +} + + +//============================================================ +// free_symbol_map +//============================================================ + +static void free_symbol_map(void) +{ + int i; + + for (i = 0; i <= map_entries; i++) + { + free(symbol_map[i].name); + symbol_map[i].name = NULL; + } +} + + +//============================================================ +// output_symbol_list +//============================================================ + +static void output_symbol_list(FILE *f) +{ + map_entry *entry; + int i; + + /* sort by hits */ + qsort(symbol_map, map_entries, sizeof(symbol_map[0]), compare_hits); + + for (i = 0, entry = symbol_map; i < map_entries; i++, entry++) + if (entry->hits > 0) + fprintf(f, "%10d %08X-%08X %s\n", entry->hits, entry->start, entry->end, entry->name); +} + + + +//============================================================ +// increment_bucket +//============================================================ + +static void increment_bucket(UINT32 addr) +{ + int i; + + for (i = 0; i < map_entries; i++) + if (addr <= symbol_map[i].end) + { + symbol_map[i].hits++; + return; + } +} + + + +//============================================================ +// profiler_thread +//============================================================ + +static DWORD WINAPI profiler_thread_entry(LPVOID lpParameter) +{ + HANDLE mainThread = (HANDLE)lpParameter; + CONTEXT context; + + /* loop until done */ + memset(&context, 0, sizeof(context)); + while (!profiler_thread_exit) + { + /* pause the main thread and get its context */ + SuspendThread(mainThread); + context.ContextFlags = CONTEXT_FULL; + GetThreadContext(mainThread, &context); + ResumeThread(mainThread); + + /* add to the bucket */ + increment_bucket(context.Eip); + + /* sleep */ + Sleep(1); + } + + return 0; +} + + + +//============================================================ +// start_profiler +//============================================================ + +static void start_profiler(void) +{ + HANDLE currentThread; + BOOL result; + + // parse the map file, if present + parse_map_file(); + + /* do the dance to get a handle to ourself */ + result = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), ¤tThread, + THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, FALSE, 0); + assert_always(result, "Failed to get thread handle for main thread"); + + profiler_thread_exit = 0; + + /* start the thread */ + profiler_thread = CreateThread(NULL, 0, profiler_thread_entry, (LPVOID)currentThread, 0, &profiler_thread_id); + assert_always(profiler_thread, "Failed to create profiler thread\n"); + + /* max out the priority */ + SetThreadPriority(profiler_thread, THREAD_PRIORITY_TIME_CRITICAL); +} + + + +//============================================================ +// stop_profiler +//============================================================ + +static void stop_profiler(void) +{ + profiler_thread_exit = 1; + WaitForSingleObject(profiler_thread, 2000); + output_symbol_list(stderr); + free_symbol_map(); +} +#else +static void start_profiler(void) {} +static void stop_profiler(void) {} +#endif diff --git a/src/osd/windows/winmain.h b/src/osd/windows/winmain.h new file mode 100644 index 00000000000..5bc3a100f63 --- /dev/null +++ b/src/osd/windows/winmain.h @@ -0,0 +1,68 @@ +//============================================================ +// +// winmain.h - Win32 main program and core headers +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + + +//============================================================ +// CONSTANTS +//============================================================ + +// video options +#define WINOPTION_D3DVERSION "d3dversion" +#define WINOPTION_VIDEO "video" +#define WINOPTION_PRESCALE "prescale" +#define WINOPTION_NUMSCREENS "numscreens" +#define WINOPTION_WAITVSYNC "waitvsync" +#define WINOPTION_TRIPLEBUFFER "triplebuffer" +#define WINOPTION_HWSTRETCH "hwstretch" +#define WINOPTION_SWITCHRES "switchres" +#define WINOPTION_KEEPASPECT "keepaspect" +#define WINOPTION_SYNCREFRESH "syncrefresh" +#define WINOPTION_FULLSCREENGAMMA "full_screen_gamma" +#define WINOPTION_FULLSCREENBRIGHTNESS "full_screen_brightness" +#define WINOPTION_FULLLSCREENCONTRAST "full_screen_contrast" +#define WINOPTION_EFFECT "effect" +#define WINOPTION_ASPECT "aspect" +#define WINOPTION_RESOLUTION "resolution" +#define WINOPTION_RESOLUTION0 "resolution0" +#define WINOPTION_RESOLUTION1 "resolution1" +#define WINOPTION_RESOLUTION2 "resolution2" +#define WINOPTION_RESOLUTION3 "resolution3" +#define WINOPTION_FILTER "filter" +#define WINOPTION_SCREEN "screen" + +// window options +#define WINOPTION_VIEW "view" +#define WINOPTION_MAXIMIZE "maximize" +#define WINOPTION_WINDOW "window" + +// input options +#define WINOPTION_DUAL_LIGHTGUN "dual_lightgun" + +// misc options +#define WINOPTION_AUDIO_LATENCY "audio_latency" +#define WINOPTION_PRIORITY "priority" +#define WINOPTION_MULTITHREADING "multithreading" +#define WINOPTION_OSLOG "oslog" + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +extern const options_entry mame_win_options[]; + + + +//============================================================ +// FUNCTION PROTOTYPES +//============================================================ + +// use if you want to print something with the verbose flag +void CLIB_DECL mame_printf_verbose(const char *text, ...) ATTR_PRINTF(1,2); diff --git a/src/osd/windows/winmisc.c b/src/osd/windows/winmisc.c new file mode 100644 index 00000000000..486066c864b --- /dev/null +++ b/src/osd/windows/winmisc.c @@ -0,0 +1,65 @@ +//============================================================ +// +// winmisc.c - Win32 OSD core miscellaneous functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define _WIN32_WINNT 0x0400 +#define WIN32_LEAN_AND_MEAN +#include +#include + +// MAME headers +#include "osdcore.h" + +// MAMEOS headers +#include "winutf8.h" +#include "strconv.h" + + +//============================================================ +// osd_alloc_executable +//============================================================ + +void *osd_alloc_executable(size_t size) +{ + return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); +} + + +//============================================================ +// osd_free_executable +//============================================================ + +void osd_free_executable(void *ptr, size_t size) +{ + VirtualFree(ptr, 0, MEM_RELEASE); +} + + +//============================================================ +// osd_is_bad_read_ptr +//============================================================ + +int osd_is_bad_read_ptr(const void *ptr, size_t size) +{ + return IsBadReadPtr(ptr, size); +} + + +//============================================================ +// osd_break_into_debugger +//============================================================ + +void osd_break_into_debugger(const char *message) +{ + if (IsDebuggerPresent()) + { + win_output_debug_string_utf8(message); + DebugBreak(); + } +} diff --git a/src/osd/windows/winprefix.h b/src/osd/windows/winprefix.h new file mode 100644 index 00000000000..a902120f2e7 --- /dev/null +++ b/src/osd/windows/winprefix.h @@ -0,0 +1,35 @@ +//============================================================ +// +// winprefix.h - Win32 prefix file, included by ALL files +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifdef MALLOC_DEBUG +#include + +// override malloc to track file/line +void* malloc_file_line(size_t size, const char *file, int line); +void* calloc_file_line(size_t size, size_t count, const char *FILE, int line); +void * realloc_file_line(void *memory, size_t size, const char *file, int line); + +#undef malloc +#define malloc(x) malloc_file_line(x, __FILE__, __LINE__) +#undef calloc +#define calloc(x,y) calloc_file_line(x, y, __FILE__, __LINE__) +#undef realloc +#define realloc(x,y) realloc_file_line(x, y, __FILE__, __LINE__) +#endif + +#ifdef _MSC_VER +void *__cdecl _alloca(size_t); +#define alloca _alloca +#endif + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#endif + +#define PATH_SEPARATOR "\\" diff --git a/src/osd/windows/winsync.c b/src/osd/windows/winsync.c new file mode 100644 index 00000000000..4d86d2ef061 --- /dev/null +++ b/src/osd/windows/winsync.c @@ -0,0 +1,157 @@ +//============================================================ +// +// winsync.c - Win32 OSD core synchronization functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif +#define WIN32_LEAN_AND_MEAN +#include + +// MAME headers +#include "osdcore.h" + + +//============================================================ +// DEBUGGING +//============================================================ + +#define DEBUG_SLOW_LOCKS 0 + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef BOOL (WINAPI *try_enter_critical_section_ptr)(LPCRITICAL_SECTION lpCriticalSection); + +struct _osd_lock +{ + CRITICAL_SECTION critsect; +}; + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +static try_enter_critical_section_ptr try_enter_critical_section = NULL; +static int checked_for_try_enter = FALSE; + + + +//============================================================ +// osd_lock_alloc +//============================================================ + +osd_lock *osd_lock_alloc(void) +{ + osd_lock *lock = malloc(sizeof(*lock)); + if (lock == NULL) + return NULL; + InitializeCriticalSection(&lock->critsect); + return lock; +} + + +//============================================================ +// osd_lock_acquire +//============================================================ + +void osd_lock_acquire(osd_lock *lock) +{ +#if DEBUG_SLOW_LOCKS + osd_ticks_t ticks = osd_ticks(); +#endif + + // block until we can acquire the lock + EnterCriticalSection(&lock->critsect); + +#if DEBUG_SLOW_LOCKS + // log any locks that take more than 1ms + ticks = osd_ticks() - ticks; + if (ticks > osd_ticks_per_second() / 1000) mame_printf_debug("Blocked %d ticks on lock acquire\n", (int)ticks); +#endif +} + + +//============================================================ +// osd_lock_try +//============================================================ + +int osd_lock_try(osd_lock *lock) +{ + int result = TRUE; + + // if we haven't yet checked for the TryEnter API, do it now + if (!checked_for_try_enter) + { + // see if we can use TryEnterCriticalSection + HMODULE library = LoadLibrary(TEXT("kernel32.dll")); + if (library != NULL) + try_enter_critical_section = (try_enter_critical_section_ptr)GetProcAddress(library, "TryEnterCriticalSection"); + checked_for_try_enter = TRUE; + } + + // if we have it, use it, otherwise just block + if (try_enter_critical_section != NULL) + result = (*try_enter_critical_section)(&lock->critsect); + else + EnterCriticalSection(&lock->critsect); + return result; +} + + +//============================================================ +// osd_lock_release +//============================================================ + +void osd_lock_release(osd_lock *lock) +{ + LeaveCriticalSection(&lock->critsect); +} + + +//============================================================ +// osd_lock_free +//============================================================ + +void osd_lock_free(osd_lock *lock) +{ + DeleteCriticalSection(&lock->critsect); + free(lock); +} + + +//============================================================ +// osd_compare_exchange32 +//============================================================ + +#ifndef osd_compare_exchange32 +INT32 osd_compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange) +{ + return InterlockedCompareExchange((LPLONG)ptr, (LONG)exchange, (LONG)compare); +} +#endif + + +//============================================================ +// osd_compare_exchange64 +//============================================================ + +#ifdef PTR64 +#ifndef osd_compare_exchange64 +INT64 osd_compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange) +{ + return InterlockedCompareExchange64((LONGLONG*)ptr, (LONGLONG)exchange, (LONGLONG)compare); +} +#endif +#endif diff --git a/src/osd/windows/wintime.c b/src/osd/windows/wintime.c new file mode 100644 index 00000000000..4a92b237ba6 --- /dev/null +++ b/src/osd/windows/wintime.c @@ -0,0 +1,159 @@ +//============================================================ +// +// wintime.c - Win32 OSD core timing functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include + +// MAME headers +#include "osdepend.h" + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +static osd_ticks_t ticks_per_second = 0; +static osd_ticks_t suspend_ticks = 0; +static BOOL using_qpc = TRUE; + + + +//============================================================ +// osd_ticks +//============================================================ + +osd_ticks_t osd_ticks(void) +{ + LARGE_INTEGER performance_count; + + // if we're suspended, just return that + if (suspend_ticks != 0) + return suspend_ticks; + + // if we have a per second count, just go for it + if (ticks_per_second != 0) + { + // QueryPerformanceCounter if we can + if (using_qpc) + { + QueryPerformanceCounter(&performance_count); + return (osd_ticks_t)performance_count.QuadPart - suspend_ticks; + } + + // otherwise, fall back to timeGetTime + else + return (osd_ticks_t)timeGetTime() - suspend_ticks; + } + + // if not, we have to determine it + using_qpc = QueryPerformanceFrequency(&performance_count) && (performance_count.QuadPart != 0); + if (using_qpc) + ticks_per_second = (osd_ticks_t)performance_count.QuadPart; + else + ticks_per_second = 1000; + + // call ourselves to get the first value + return osd_ticks(); +} + + +//============================================================ +// osd_ticks_per_second +//============================================================ + +osd_ticks_t osd_ticks_per_second(void) +{ + return ticks_per_second; +} + + +//============================================================ +// osd_profiling_ticks +//============================================================ + +#ifdef _MSC_VER + +#ifdef PTR64 + +osd_ticks_t osd_profiling_ticks(void) +{ + return __rdtsc(); +} + +#else + +osd_ticks_t osd_profiling_ticks(void) +{ + INT64 result; + INT64 *presult = &result; + + __asm { + __asm _emit 0Fh __asm _emit 031h // rdtsc + mov ebx, presult + mov [ebx],eax + mov [ebx+4],edx + } + + return result; +} + +#endif + +#else + +osd_ticks_t osd_profiling_ticks(void) +{ + INT64 result; + + // use RDTSC + __asm__ __volatile__ ( + "rdtsc" + : "=A" (result) + ); + + return result; +} + +#endif + + +//============================================================ +// osd_sleep +//============================================================ + +void osd_sleep(osd_ticks_t duration) +{ + DWORD msec; + + // make sure we've computed ticks_per_second + if (ticks_per_second == 0) + (void)osd_ticks(); + + // convert to milliseconds, rounding down + msec = (DWORD)(duration * 1000 / ticks_per_second); + + // only sleep if at least 2 full milliseconds + if (msec >= 2) + { + HANDLE current_thread = GetCurrentThread(); + int old_priority = GetThreadPriority(current_thread); + + // take a couple of msecs off the top for good measure + msec -= 2; + + // bump our thread priority super high so that we get + // priority when we need it + SetThreadPriority(current_thread, THREAD_PRIORITY_TIME_CRITICAL); + Sleep(msec); + SetThreadPriority(current_thread, old_priority); + } +} diff --git a/src/osd/windows/winutf8.c b/src/osd/windows/winutf8.c new file mode 100644 index 00000000000..38f67555402 --- /dev/null +++ b/src/osd/windows/winutf8.c @@ -0,0 +1,260 @@ +//============================================================ +// +// winutf8.c - Win32 OSD core utility functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define _WIN32_WINNT 0x0400 +#define WIN32_LEAN_AND_MEAN +#include +#include + +// MAMEOS headers +#include "winutf8.h" +#include "strconv.h" + + +//============================================================ +// win_output_debug_string_utf8 +//============================================================ + +void win_output_debug_string_utf8(const char *string) +{ + TCHAR *t_string = tstring_from_utf8(string); + if (t_string != NULL) + { + OutputDebugString(t_string); + free(t_string); + } +} + + + +//============================================================ +// win_get_module_file_name_utf8 +//============================================================ + +DWORD win_get_module_file_name_utf8(HMODULE module, char *filename, DWORD size) +{ + TCHAR t_filename[MAX_PATH]; + char *utf8_filename; + + if (GetModuleFileName(module, t_filename, ARRAY_LENGTH(t_filename)) == 0) + return 0; + + utf8_filename = utf8_from_tstring(t_filename); + if (!utf8_filename) + return 0; + + size = (DWORD) snprintf(filename, size, "%s", utf8_filename); + free(utf8_filename); + return size; +} + + + +//============================================================ +// win_set_file_attributes_utf8 +//============================================================ + +BOOL win_set_file_attributes_utf8(const char* filename, DWORD fileattributes) +{ + BOOL result = FALSE; + TCHAR* t_filename = tstring_from_utf8(filename); + if( !t_filename ) + return result; + + result = SetFileAttributes(t_filename, fileattributes); + + free(t_filename); + + return result; +} + + + +//============================================================ +// win_copy_file_utf8 +//============================================================ + +BOOL win_copy_file_utf8(const char* existingfilename, const char* newfilename, BOOL failifexists) +{ + TCHAR* t_existingfilename; + TCHAR* t_newfilename; + BOOL result = FALSE; + + t_existingfilename = tstring_from_utf8(existingfilename); + if( !t_existingfilename ) + return result; + + t_newfilename = tstring_from_utf8(newfilename); + if( !t_newfilename ) { + free(t_existingfilename); + return result; + } + + result = CopyFile(t_existingfilename, t_newfilename, failifexists); + + free(t_newfilename); + free(t_existingfilename); + + return result; +} + + + +//============================================================ +// win_move_file_utf8 +//============================================================ + +BOOL win_move_file_utf8(const char* existingfilename, const char* newfilename) +{ + TCHAR* t_existingfilename; + TCHAR* t_newfilename; + BOOL result = FALSE; + + t_existingfilename = tstring_from_utf8(existingfilename); + if( !t_existingfilename ) + return result; + + t_newfilename = tstring_from_utf8(newfilename); + if( !t_newfilename ) { + free(t_existingfilename); + return result; + } + + result = MoveFile(t_existingfilename, t_newfilename); + + free(t_newfilename); + free(t_existingfilename); + + return result; +} + + + +//============================================================ +// win_message_box_utf8 +//============================================================ + +int win_message_box_utf8(HWND window, const char *text, const char *caption, UINT type) +{ + int result = IDNO; + TCHAR *t_text = NULL; + TCHAR *t_caption = NULL; + + if (text) + { + t_text = tstring_from_utf8(text); + if (!t_text) + goto done; + } + + if (caption) + { + t_caption = tstring_from_utf8(caption); + if (!t_caption) + goto done; + } + + result = MessageBox(window, t_text, t_caption, type); + +done: + if (t_text) + free(t_text); + if (t_caption) + free(t_caption); + return result; +} + + + +//============================================================ +// win_set_window_text_utf8 +//============================================================ + +BOOL win_set_window_text_utf8(HWND window, const char *text) +{ + BOOL result = FALSE; + TCHAR *t_text = NULL; + + if (text) + { + t_text = tstring_from_utf8(text); + if (!t_text) + goto done; + } + + result = SetWindowText(window, t_text); + +done: + if (t_text) + free(t_text); + return result; +} + + + +//============================================================ +// win_get_window_text_utf8 +//============================================================ + +int win_get_window_text_utf8(HWND window, char *buffer, size_t buffer_size) +{ + int result = 0; + char *utf8_buffer = NULL; + TCHAR t_buffer[256]; + + t_buffer[0] = '\0'; + + // invoke the core Win32 API + GetWindowText(window, t_buffer, ARRAY_LENGTH(t_buffer)); + + utf8_buffer = utf8_from_tstring(t_buffer); + if (!utf8_buffer) + goto done; + + result = snprintf(buffer, buffer_size, "%s", utf8_buffer); + +done: + if (utf8_buffer) + free(utf8_buffer); + return result; +} + + + +//============================================================ +// win_create_window_ex_utf8 +//============================================================ + +HWND win_create_window_ex_utf8(DWORD exstyle, const char* classname, const char* windowname, DWORD style, + int x, int y, int width, int height, HWND parent, HMENU menu, + HINSTANCE instance, void* param) +{ + TCHAR* t_classname; + TCHAR* t_windowname; + HWND result = 0; + + t_classname = tstring_from_utf8(classname); + if( !t_classname ) + return result; + + t_windowname = tstring_from_utf8(windowname); + if( !t_windowname ) { + free(t_classname); + return result; + } + + result = CreateWindowEx(exstyle, t_classname, t_windowname, style, x, y, width, height, parent, + menu, instance, param); + + free(t_windowname); + free(t_classname); + + return result; +} diff --git a/src/osd/windows/winutf8.h b/src/osd/windows/winutf8.h new file mode 100644 index 00000000000..137e111f957 --- /dev/null +++ b/src/osd/windows/winutf8.h @@ -0,0 +1,30 @@ +//============================================================ +// +// winutf8.h - Win32 UTF-8 wrappers +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __WINUTF8__ +#define __WINUTF8__ + +#include "osdcore.h" + +// wrappers for kernel32.dll +void win_output_debug_string_utf8(const char *string); +DWORD win_get_module_file_name_utf8(HMODULE module, char *filename, DWORD size); +BOOL win_set_file_attributes_utf8(const char* filename, DWORD fileattributes); +BOOL win_copy_file_utf8(const char* existingfilename, const char* newfilename, BOOL failifexists); +BOOL win_move_file_utf8(const char* existingfilename, const char* newfilename); + +// wrappers for user32.dll +int win_message_box_utf8(HWND window, const char *text, const char *caption, UINT type); +BOOL win_set_window_text_utf8(HWND window, const char *text); +int win_get_window_text_utf8(HWND window, char *buffer, size_t buffer_size); +HWND win_create_window_ex_utf8(DWORD exstyle, const char* classname, const char* windowname, DWORD style, + int x, int y, int width, int height, HWND wndparent, HMENU menu, + HINSTANCE instance, void* param); + +#endif // __WINUTF8__ diff --git a/src/osd/windows/winutil.c b/src/osd/windows/winutil.c new file mode 100644 index 00000000000..7197bd2303f --- /dev/null +++ b/src/osd/windows/winutil.c @@ -0,0 +1,125 @@ +//============================================================ +// +// winutil.c - Win32 OSD core utility functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define _WIN32_WINNT 0x0400 +#define WIN32_LEAN_AND_MEAN +#include + +// MAMEOS headers +#include "winutil.h" +#include "strconv.h" + +//============================================================ +// win_error_to_file_error +//============================================================ + +file_error win_error_to_file_error(DWORD error) +{ + file_error filerr; + + // convert a Windows error to a file_error + switch (error) + { + case ERROR_SUCCESS: + filerr = FILERR_NONE; + break; + + case ERROR_OUTOFMEMORY: + filerr = FILERR_OUT_OF_MEMORY; + break; + + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + filerr = FILERR_NOT_FOUND; + break; + + case ERROR_ACCESS_DENIED: + filerr = FILERR_ACCESS_DENIED; + break; + + case ERROR_SHARING_VIOLATION: + filerr = FILERR_ALREADY_OPEN; + break; + + default: + filerr = FILERR_FAILURE; + break; + } + return filerr; +} + + + +//============================================================ +// win_attributes_to_entry_type +//============================================================ + +osd_dir_entry_type win_attributes_to_entry_type(DWORD attributes) +{ + if (attributes == 0xFFFFFFFF) + return ENTTYPE_NONE; + else if (attributes & FILE_ATTRIBUTE_DIRECTORY) + return ENTTYPE_DIR; + else + return ENTTYPE_FILE; +} + + + +//============================================================ +// win_is_gui_application +//============================================================ + +BOOL win_is_gui_application(void) +{ + static BOOL is_gui_frontend; + static BOOL is_first_time = TRUE; + HMODULE module; + BYTE *image_ptr; + IMAGE_DOS_HEADER *dos_header; + IMAGE_NT_HEADERS *nt_headers; + IMAGE_OPTIONAL_HEADER *opt_header; + + // is this the first time we've been ran? + if (is_first_time) + { + is_first_time = FALSE; + + // get the current module + module = GetModuleHandle(NULL); + if (!module) + return FALSE; + image_ptr = (BYTE*) module; + + // access the DOS header + dos_header = (IMAGE_DOS_HEADER *) image_ptr; + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) + return FALSE; + + // access the NT headers + nt_headers = (IMAGE_NT_HEADERS *) ((BYTE*)(dos_header) + (DWORD)(dos_header->e_lfanew)); + if (nt_headers->Signature != IMAGE_NT_SIGNATURE) + return FALSE; + + // access the optional header + opt_header = &nt_headers->OptionalHeader; + switch (opt_header->Subsystem) + { + case IMAGE_SUBSYSTEM_WINDOWS_GUI: + is_gui_frontend = TRUE; + break; + + case IMAGE_SUBSYSTEM_WINDOWS_CUI: + is_gui_frontend = FALSE; + break; + } + } + return is_gui_frontend; +} diff --git a/src/osd/windows/winutil.h b/src/osd/windows/winutil.h new file mode 100644 index 00000000000..412686cb36b --- /dev/null +++ b/src/osd/windows/winutil.h @@ -0,0 +1,20 @@ +//============================================================ +// +// winutil.h - Win32 OSD core utility functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +#ifndef __WINUTIL__ +#define __WINUTIL__ + +#include "osdcore.h" + +// Shared code +file_error win_error_to_file_error(DWORD error); +osd_dir_entry_type win_attributes_to_entry_type(DWORD attributes); +BOOL win_is_gui_application(void); + +#endif // __WINUTIL__ diff --git a/src/osd/windows/winwork.c b/src/osd/windows/winwork.c new file mode 100644 index 00000000000..602c25a15fc --- /dev/null +++ b/src/osd/windows/winwork.c @@ -0,0 +1,775 @@ +//============================================================ +// +// winwork.c - Win32 OSD core work item functions +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// standard windows headers +#define _WIN32_WINNT 0x0400 +#define WIN32_LEAN_AND_MEAN +#include +#include + +#ifdef __GNUC__ +#include +#endif + +// MAME headers +#include "osdcore.h" + + +//============================================================ +// DEBUGGING +//============================================================ + +#define KEEP_STATISTICS (0) + + + +//============================================================ +// PARAMETERS +//============================================================ + +#define SPIN_LOOP_TIME (osd_ticks_per_second() / 1000) + + + +//============================================================ +// MACROS +//============================================================ + +#if KEEP_STATISTICS +#define add_to_stat(v,x) do { interlocked_add((v), (x)); } while (0) +#define begin_timing(v) do { (v) -= osd_profiling_ticks(); } while (0) +#define end_timing(v) do { (v) += osd_profiling_ticks(); } while (0) +#else +#define add_to_stat(v,x) do { } while (0) +#define begin_timing(v) do { } while (0) +#define end_timing(v) do { } while (0) +#endif + +#ifndef YieldProcessor +#ifdef __GNUC__ +INLINE void YieldProcessor(void) +{ + __asm__ __volatile__ ( "rep; nop" ); +} +#else +INLINE void YieldProcessor(void) +{ + __asm { rep nop } +} +#endif +#endif + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef struct _scalable_lock scalable_lock; +struct _scalable_lock +{ + struct + { + volatile INT32 haslock; // do we have the lock? + INT32 filler[64/4-1]; // assumes a 64-byte cache line + } slot[WORK_MAX_THREADS]; // one slot per thread + volatile INT32 nextindex; // index of next slot to use +}; + + +typedef struct _work_thread_info work_thread_info; +struct _work_thread_info +{ + osd_work_queue * queue; // pointer back to the queue + HANDLE handle; // handle to the thread + HANDLE wakeevent; // wake event for the thread + volatile INT32 active; // are we actively processing work? + +#if KEEP_STATISTICS + INT32 itemsdone; + osd_ticks_t actruntime; + osd_ticks_t runtime; + osd_ticks_t spintime; + osd_ticks_t waittime; +#endif +}; + + +struct _osd_work_queue +{ + scalable_lock lock; // lock for protecting the queue + osd_work_item * volatile list; // list of items in the queue + osd_work_item ** volatile tailptr; // pointer to the tail pointer of work items in the queue + osd_work_item * volatile free; // free list of work items + volatile INT32 items; // items in the queue + volatile INT32 livethreads; // number of live threads + volatile INT32 waiting; // is someone waiting on the queue to complete? + volatile UINT8 exiting; // should the threads exit on their next opportunity? + UINT32 threads; // number of threads in this queue + UINT32 flags; // creation flags + work_thread_info * thread; // array of thread information + HANDLE doneevent; // event signalled when work is complete + +#if KEEP_STATISTICS + volatile INT32 itemsqueued; // total items queued + volatile INT32 setevents; // number of times we called SetEvent + volatile INT32 extraitems; // how many extra items we got after the first in the queue loop + volatile INT32 spinloops; // how many times spinning bought us more items +#endif +}; + + +struct _osd_work_item +{ + osd_work_item * next; // pointer to next item + osd_work_queue * queue; // pointer back to the owning queue + osd_work_callback callback; // callback function + void * param; // callback parameter + void * result; // callback result + HANDLE event; // event signalled when complete + UINT32 flags; // creation flags + volatile INT32 done; // is the item done? +}; + + + +//============================================================ +// FUNCTION PROTOTYPES +//============================================================ + +static int effective_num_processors(void); +static unsigned __stdcall worker_thread_entry(void *param); +static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread); + + + +//============================================================ +// INLINE FUNCTIONS +//============================================================ + +INLINE void *compare_exchange_ptr(void * volatile *ptr, void *compare, void *exchange) +{ +#ifdef PTR64 + INT64 result = InterlockedCompareExchange64((LONGLONG *)ptr, (LONGLONG)exchange, (LONGLONG)compare); + return (void *)result; +#else + INT32 result = InterlockedCompareExchange((LPLONG)ptr, (LONG)exchange, (LONG)compare); + return (void *)result; +#endif +} + + +INLINE INT32 interlocked_exchange32(INT32 volatile *ptr, INT32 value) +{ + return InterlockedExchange((LPLONG)ptr, value); +} + + +INLINE INT32 interlocked_increment(INT32 volatile *ptr) +{ + return InterlockedIncrement((LPLONG)ptr); +} + + +INLINE INT32 interlocked_decrement(INT32 volatile *ptr) +{ + return InterlockedDecrement((LPLONG)ptr); +} + + +INLINE INT32 interlocked_add(INT32 volatile *ptr, INT32 add) +{ + return InterlockedExchangeAdd((LPLONG)ptr, add) + add; +} + + + +//============================================================ +// Scalable Locks +//============================================================ + +INLINE void scalable_lock_init(scalable_lock *lock) +{ + memset(lock, 0, sizeof(*lock)); + lock->slot[0].haslock = TRUE; +} + + +INLINE INT32 scalable_lock_acquire(scalable_lock *lock) +{ + INT32 myslot = (interlocked_increment(&lock->nextindex) - 1) & (WORK_MAX_THREADS - 1); + INT32 backoff = 1; + + while (!lock->slot[myslot].haslock) + { + INT32 backcount; + for (backcount = 0; backcount < backoff; backcount++) + YieldProcessor(); + backoff <<= 1; + } + lock->slot[myslot].haslock = FALSE; + return myslot; +} + + +INLINE void scalable_lock_release(scalable_lock *lock, INT32 myslot) +{ + interlocked_exchange32(&lock->slot[(myslot + 1) & (WORK_MAX_THREADS - 1)].haslock, TRUE); +} + + + +//============================================================ +// osd_work_queue_alloc +//============================================================ + +osd_work_queue *osd_work_queue_alloc(int flags) +{ + int numprocs = effective_num_processors(); + osd_work_queue *queue; + int threadnum; + + // allocate a new queue + queue = malloc(sizeof(*queue)); + if (queue == NULL) + goto error; + memset(queue, 0, sizeof(*queue)); + + // initialize basic queue members + queue->tailptr = (osd_work_item **)&queue->list; + queue->flags = flags; + + // allocate events for the queue + queue->doneevent = CreateEvent(NULL, TRUE, TRUE, NULL); // manual reset, signalled + if (queue->doneevent == NULL) + goto error; + + // initialize the critical section + scalable_lock_init(&queue->lock); + + // determine how many threads to create... + // on a single-CPU system, create 1 thread for I/O queues, and 0 threads for everything else + if (numprocs == 1) + queue->threads = (flags & WORK_QUEUE_FLAG_IO) ? 1 : 0; + + // on an n-CPU system, create (n-1) threads for multi queues, and 1 thread for everything else + else + queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? (numprocs - 1) : 1; + + // clamp to the maximum + queue->threads = MIN(queue->threads, WORK_MAX_THREADS); + + // allocate memory for thread array (+1 to count the calling thread) + queue->thread = malloc((queue->threads + 1) * sizeof(queue->thread[0])); + if (queue->thread == NULL) + goto error; + memset(queue->thread, 0, (queue->threads + 1) * sizeof(queue->thread[0])); + + // iterate over threads + for (threadnum = 0; threadnum < queue->threads; threadnum++) + { + work_thread_info *thread = &queue->thread[threadnum]; + uintptr_t handle; + + // set a pointer back to the queue + thread->queue = queue; + + // create the per-thread wake event + thread->wakeevent = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset, not signalled + if (thread->wakeevent == NULL) + goto error; + + // create the thread + handle = _beginthreadex(NULL, 0, worker_thread_entry, thread, 0, NULL); + thread->handle = (HANDLE)handle; + if (thread->handle == NULL) + goto error; + + // set its priority: I/O threads get high priority because they are assumed to be + // blocked most of the time; other threads just match the creator's priority + if (flags & WORK_QUEUE_FLAG_IO) + SetThreadPriority(thread->handle, THREAD_PRIORITY_ABOVE_NORMAL); + else + SetThreadPriority(thread->handle, GetThreadPriority(GetCurrentThread())); + } + + // start a timer going for "waittime" on the main thread + begin_timing(queue->thread[queue->threads].waittime); + return queue; + +error: + osd_work_queue_free(queue); + return NULL; +} + + +//============================================================ +// osd_work_queue_items +//============================================================ + +int osd_work_queue_items(osd_work_queue *queue) +{ + // return the number of items currently in the queue + return queue->items; +} + + +//============================================================ +// osd_work_queue_wait +//============================================================ + +int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout) +{ + // if no threads, no waiting + if (queue->threads == 0) + return TRUE; + + // if no items, we're done + if (queue->items == 0) + return TRUE; + + // if this is a multi queue, help out rather than doing nothing + if (queue->flags & WORK_QUEUE_FLAG_MULTI) + { + work_thread_info *thread = &queue->thread[queue->threads]; + osd_ticks_t stopspin = osd_ticks() + timeout; + + end_timing(thread->waittime); + + // process what we can as a worker thread + worker_thread_process(queue, thread); + + // if we're a high frequency queue, spin until done + if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ) + { + // spin until we're done + begin_timing(thread->spintime); + while (queue->items != 0 && osd_ticks() < stopspin) + YieldProcessor(); + end_timing(thread->spintime); + + begin_timing(thread->waittime); + return (queue->items == 0); + } + begin_timing(thread->waittime); + } + + // reset our done event and double-check the items before waiting + ResetEvent(queue->doneevent); + interlocked_exchange32(&queue->waiting, TRUE); + if (queue->items != 0) + WaitForSingleObject(queue->doneevent, timeout * 1000 / osd_ticks_per_second()); + interlocked_exchange32(&queue->waiting, FALSE); + + // return TRUE if we actually hit 0 + return (queue->items == 0); +} + + +//============================================================ +// osd_work_queue_free +//============================================================ + +void osd_work_queue_free(osd_work_queue *queue) +{ + // if we have threads, clean them up + if (queue->threads > 0 && queue->thread != NULL) + { + int threadnum; + + // stop the timer for "waittime" on the main thread + end_timing(queue->thread[queue->threads].waittime); + + // signal all the threads to exit + queue->exiting = TRUE; + for (threadnum = 0; threadnum < queue->threads; threadnum++) + { + work_thread_info *thread = &queue->thread[threadnum]; + if (thread->wakeevent != NULL) + SetEvent(thread->wakeevent); + } + + // wait for all the threads to go away + for (threadnum = 0; threadnum < queue->threads; threadnum++) + { + work_thread_info *thread = &queue->thread[threadnum]; + + // block on the thread going away, then close the handle + if (thread->handle != NULL) + { + WaitForSingleObject(thread->handle, INFINITE); + CloseHandle(thread->handle); + } + + // clean up the wake event + if (thread->wakeevent != NULL) + CloseHandle(thread->wakeevent); + } + +#if KEEP_STATISTICS + // output per-thread statistics + for (threadnum = 0; threadnum <= queue->threads; threadnum++) + { + work_thread_info *thread = &queue->thread[threadnum]; + osd_ticks_t total = thread->runtime + thread->waittime + thread->spintime; + printf("Thread %d: items=%9d run=%5.2f%% (%5.2f%%) spin=%5.2f%% wait/other=%5.2f%%\n", + threadnum, thread->itemsdone, + (double)thread->runtime * 100.0 / (double)total, + (double)thread->actruntime * 100.0 / (double)total, + (double)thread->spintime * 100.0 / (double)total, + (double)thread->waittime * 100.0 / (double)total); + } +#endif + + // free the list + free(queue->thread); + } + + // free all the events + if (queue->doneevent != NULL) + CloseHandle(queue->doneevent); + + // free all items in the free list + while (queue->free != NULL) + { + osd_work_item *item = (osd_work_item *)queue->free; + queue->free = item->next; + if (item->event != NULL) + CloseHandle(item->event); + free(item); + } + + // free all items in the active list + while (queue->list != NULL) + { + osd_work_item *item = (osd_work_item *)queue->list; + queue->list = item->next; + if (item->event != NULL) + CloseHandle(item->event); + free(item); + } + +#if KEEP_STATISTICS + printf("Items queued = %9d\n", queue->itemsqueued); + printf("SetEvent calls = %9d\n", queue->setevents); + printf("Extra items = %9d\n", queue->extraitems); + printf("Spin loops = %9d\n", queue->spinloops); +#endif + + // free the queue itself + free(queue); +} + + +//============================================================ +// osd_work_item_queue_multiple +//============================================================ + +osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags) +{ + osd_work_item *itemlist = NULL; + osd_work_item **item_tailptr = &itemlist; + INT32 lockslot; + int itemnum; + + // loop over items, building up a local list of work + for (itemnum = 0; itemnum < numitems; itemnum++) + { + osd_work_item *item; + + // first allocate a new work item; try the free list first + do + { + item = (osd_work_item *)queue->free; + } while (item != NULL && compare_exchange_ptr((PVOID volatile *)&queue->free, item, item->next) != item); + + // if nothing, allocate something new + if (item == NULL) + { + // allocate the item + item = malloc(sizeof(*item)); + if (item == NULL) + return NULL; + item->event = NULL; + item->queue = queue; + } + + // fill in the basics + item->next = NULL; + item->callback = callback; + item->param = parambase; + item->result = NULL; + item->flags = flags; + item->done = FALSE; + + // advance to the next + *item_tailptr = item; + item_tailptr = &item->next; + parambase = (UINT8 *)parambase + paramstep; + } + + // enqueue the whole thing within the critical section + lockslot = scalable_lock_acquire(&queue->lock); + *queue->tailptr = itemlist; + queue->tailptr = item_tailptr; + scalable_lock_release(&queue->lock, lockslot); + + // increment the number of items in the queue + interlocked_add(&queue->items, numitems); + add_to_stat(&queue->itemsqueued, numitems); + + // look for free threads to do the work + if (queue->livethreads < queue->threads) + { + int threadnum; + + // iterate over all the threads + for (threadnum = 0; threadnum < queue->threads; threadnum++) + { + work_thread_info *thread = &queue->thread[threadnum]; + + // if this thread is not active, wake him up + if (!thread->active) + { + SetEvent(thread->wakeevent); + add_to_stat(&queue->setevents, 1); + + // for non-shared, the first one we find is good enough + if (--numitems == 0) + break; + } + } + } + + // if no threads, run the queue now on this thread + if (queue->threads == 0) + worker_thread_process(queue, &queue->thread[0]); + + // only return the item if it won't get released automatically + return (flags & WORK_ITEM_FLAG_AUTO_RELEASE) ? NULL : *item_tailptr; +} + + +//============================================================ +// osd_work_item_wait +//============================================================ + +int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout) +{ + // if we're done already, just return + if (item->done) + return TRUE; + + // if we don't have an event, create one + if (item->event == NULL) + item->event = CreateEvent(NULL, TRUE, FALSE, NULL); // manual reset, not signalled + else + ResetEvent(item->event); + + // if we don't have an event, we need to spin (shouldn't ever really happen) + if (item->event != NULL) + { + osd_ticks_t stopspin = osd_ticks() + timeout; + while (!item->done && osd_ticks() < stopspin) + YieldProcessor(); + } + + // otherwise, block on the event until done + else if (!item->done) + WaitForSingleObject(item->event, timeout * 1000 / osd_ticks_per_second()); + + // return TRUE if the refcount actually hit 0 + return item->done; +} + + +//============================================================ +// osd_work_item_result +//============================================================ + +void *osd_work_item_result(osd_work_item *item) +{ + return item->result; +} + + +//============================================================ +// osd_work_item_release +//============================================================ + +void osd_work_item_release(osd_work_item *item) +{ + osd_work_item *next; + + // make sure we're done first + osd_work_item_wait(item, 100 * osd_ticks_per_second()); + + // add us to the free list on our queue + do + { + next = (osd_work_item *)item->queue->free; + item->next = next; + } while (compare_exchange_ptr((PVOID volatile *)&item->queue->free, next, item) != next); +} + + +//============================================================ +// effective_num_processors +//============================================================ + +static int effective_num_processors(void) +{ + char *procsoverride; + SYSTEM_INFO info; + int numprocs = 0; + + // if the OSDPROCESSORS environment variable is set, use that value if valid + procsoverride = getenv("OSDPROCESSORS"); + if (procsoverride != NULL && sscanf(procsoverride, "%d", &numprocs) == 1 && numprocs > 0) + return numprocs; + + // otherwise, fetch the info from the system + GetSystemInfo(&info); + + // max out at 4 for now since scaling above that seems to do poorly + return MIN(info.dwNumberOfProcessors, 4); +} + + +//============================================================ +// worker_thread_entry +//============================================================ + +static unsigned __stdcall worker_thread_entry(void *param) +{ + work_thread_info *thread = param; + osd_work_queue *queue = thread->queue; + + // loop until we exit + for ( ;; ) + { + // block waiting for work or exit + DWORD result = WAIT_OBJECT_0; + + // bail on exit, and only wait if there are no pending items in queue + if (!queue->exiting && queue->list == NULL) + { + begin_timing(thread->waittime); + result = WaitForSingleObject(thread->wakeevent, INFINITE); + end_timing(thread->waittime); + } + if (queue->exiting) + break; + + // indicate that we are live + interlocked_exchange32(&thread->active, TRUE); + interlocked_increment(&queue->livethreads); + + // process work items + for ( ;; ) + { + osd_ticks_t stopspin; + + // process as much as we can + worker_thread_process(queue, thread); + + // if we're a high frequency queue, spin for a while before giving up + if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ) + { + // spin for a while looking for more work + begin_timing(thread->spintime); + stopspin = osd_ticks() + SPIN_LOOP_TIME; + while (queue->list == NULL && osd_ticks() < stopspin) + YieldProcessor(); + end_timing(thread->spintime); + } + + // if nothing more, release the processor + if (queue->list == NULL) + break; + add_to_stat(&queue->spinloops, 1); + } + + // decrement the live thread count + interlocked_exchange32(&thread->active, FALSE); + interlocked_decrement(&queue->livethreads); + } + return 0; +} + + +//============================================================ +// worker_thread_process +//============================================================ + +static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread) +{ + int threadid = thread - queue->thread; + + begin_timing(thread->runtime); + + // loop until everything is processed + while (queue->list != NULL) + { + osd_work_item *item; + INT32 lockslot; + + // use a critical section to synchronize the removal of items + lockslot = scalable_lock_acquire(&queue->lock); + { + // pull the item from the queue + item = (osd_work_item *)queue->list; + if (item != NULL) + { + queue->list = item->next; + if (queue->list == NULL) + queue->tailptr = (osd_work_item **)&queue->list; + } + } + scalable_lock_release(&queue->lock, lockslot); + + // process non-NULL items + if (item != NULL) + { + // call the callback and stash the result + begin_timing(thread->actruntime); + item->result = (*item->callback)(item->param, threadid); + end_timing(thread->actruntime); + + // decrement the item count after we are done + interlocked_decrement(&queue->items); + interlocked_exchange32(&item->done, TRUE); + add_to_stat(&thread->itemsdone, 1); + + // if it's an auto-release item, release it + if (item->flags & WORK_ITEM_FLAG_AUTO_RELEASE) + osd_work_item_release(item); + + // set the result and signal the event + else if (item->event != NULL) + { + SetEvent(item->event); + add_to_stat(&item->queue->setevents, 1); + } + + // if we removed an item and there's still work to do, bump the stats + if (queue->list != NULL) + add_to_stat(&queue->extraitems, 1); + } + } + + // we don't need to set the doneevent for multi queues because they spin + if (queue->waiting) + { + SetEvent(queue->doneevent); + add_to_stat(&queue->setevents, 1); + } + + end_timing(thread->runtime); +} -- cgit v1.2.3