Refactoring: move GUID operations out of Joystick

- SDL_JoystickGUID -> SDL_GUID (though we retain a type alias)
- Operations for GUID <-> String ops are now in
  src/SDL_guid.c and include/SDL_guid.h
- The corresponding Joystick operations delegate to SDL_guid.c
- Added test/testguid.c
This commit is contained in:
Christoph Reichenbach 2022-06-04 20:16:28 +00:00 committed by Sam Lantinga
parent 4e07d4722d
commit 3a20274ddf
18 changed files with 422 additions and 89 deletions

View file

@ -80,6 +80,7 @@ HDRS = \
SDL_filesystem.h \
SDL_gamecontroller.h \
SDL_gesture.h \
SDL_guid.h \
SDL_haptic.h \
SDL_hidapi.h \
SDL_hints.h \

View file

@ -67,7 +67,7 @@ CFLAGS_DLL+= -DSDL_BUILD_MAJOR_VERSION=$(MAJOR_VERSION)
CFLAGS_DLL+= -DSDL_BUILD_MINOR_VERSION=$(MINOR_VERSION)
CFLAGS_DLL+= -DSDL_BUILD_MICRO_VERSION=$(MICRO_VERSION)
SRCS = SDL.c SDL_assert.c SDL_error.c SDL_log.c SDL_dataqueue.c SDL_hints.c SDL_list.c
SRCS = SDL.c SDL_assert.c SDL_error.c SDL_guid.c SDL_log.c SDL_dataqueue.c SDL_hints.c SDL_list.c
SRCS+= SDL_getenv.c SDL_iconv.c SDL_malloc.c SDL_qsort.c SDL_stdlib.c SDL_string.c SDL_strtokr.c SDL_crc32.c
SRCS+= SDL_cpuinfo.c SDL_atomic.c SDL_spinlock.c SDL_thread.c SDL_timer.c
SRCS+= SDL_rwops.c SDL_power.c

View file

@ -43,7 +43,7 @@ CFLAGS_DLL+= -DSDL_BUILD_MICRO_VERSION=$(MICRO_VERSION)
RCFLAGS = -q -r -bt=nt $(INCPATH)
SRCS = SDL.c SDL_assert.c SDL_error.c SDL_log.c SDL_dataqueue.c SDL_hints.c SDL_list.c
SRCS = SDL.c SDL_assert.c SDL_error.c SDL_guid.c SDL_log.c SDL_dataqueue.c SDL_hints.c SDL_list.c
SRCS+= SDL_getenv.c SDL_iconv.c SDL_malloc.c SDL_qsort.c SDL_stdlib.c SDL_string.c SDL_strtokr.c SDL_crc32.c
SRCS+= SDL_cpuinfo.c SDL_atomic.c SDL_spinlock.c SDL_thread.c SDL_timer.c
SRCS+= SDL_rwops.c SDL_power.c

View file

@ -45,6 +45,7 @@
<ClInclude Include="..\include\SDL_error.h" />
<ClInclude Include="..\include\SDL_events.h" />
<ClInclude Include="..\include\SDL_filesystem.h" />
<ClInclude Include="..\include\SDL_guid.h" />
<ClInclude Include="..\include\SDL_haptic.h" />
<ClInclude Include="..\include\SDL_hints.h" />
<ClInclude Include="..\include\SDL_hidapi.h" />
@ -301,6 +302,7 @@
<ClCompile Include="..\src\SDL_dataqueue.c" />
<ClCompile Include="..\src\SDL_list.c" />
<ClCompile Include="..\src\SDL_error.c" />
<ClCompile Include="..\src\SDL_guid.c" />
<ClCompile Include="..\src\SDL_hints.c" />
<ClCompile Include="..\src\SDL_log.c" />
<ClCompile Include="..\src\sensor\dummy\SDL_dummysensor.c" />

View file

@ -63,6 +63,9 @@
<ClInclude Include="..\include\SDL_filesystem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\SDL_guid.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\SDL_haptic.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -633,6 +636,9 @@
<ClCompile Include="..\src\SDL_error.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\SDL_guid.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\SDL_hints.c">
<Filter>Source Files</Filter>
</ClCompile>

View file

@ -234,6 +234,7 @@
<ClInclude Include="..\..\include\SDL_filesystem.h" />
<ClInclude Include="..\..\include\SDL_gamecontroller.h" />
<ClInclude Include="..\..\include\SDL_gesture.h" />
<ClInclude Include="..\..\include\SDL_guid.h" />
<ClInclude Include="..\..\include\SDL_haptic.h" />
<ClInclude Include="..\..\include\SDL_hints.h" />
<ClInclude Include="..\..\include\SDL_hidapi.h" />
@ -367,6 +368,7 @@
<ClInclude Include="..\..\src\SDL_assert_c.h" />
<ClInclude Include="..\..\src\SDL_dataqueue.h" />
<ClInclude Include="..\..\src\SDL_error_c.h" />
<ClCompile Include="..\..\src\SDL_guid.c" />
<ClInclude Include="..\..\src\SDL_hints_c.h" />
<ClInclude Include="..\..\src\SDL_internal.h" />
<ClInclude Include="..\..\src\SDL_list.h" />

View file

@ -231,6 +231,9 @@
<ClInclude Include="..\..\include\SDL_gesture.h">
<Filter>API Headers</Filter>
</ClInclude>
<ClInclude Include="..\..\include\SDL_guid.h">
<Filter>API Headers</Filter>
</ClInclude>
<ClInclude Include="..\..\include\SDL_haptic.h">
<Filter>API Headers</Filter>
</ClInclude>
@ -833,6 +836,7 @@
<ClCompile Include="..\..\src\SDL_assert.c" />
<ClCompile Include="..\..\src\SDL_dataqueue.c" />
<ClCompile Include="..\..\src\SDL_error.c" />
<ClCompile Include="..\..\src\SDL_guid.c" />
<ClCompile Include="..\..\src\SDL_hints.c" />
<ClCompile Include="..\..\src\SDL_list.c" />
<ClCompile Include="..\..\src\audio\SDL_audio.c">

100
include/SDL_guid.h Normal file
View file

@ -0,0 +1,100 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* \file SDL_guid.h
*
* Include file for handling ::SDL_GUID values.
*/
#ifndef SDL_guid_h_
#define SDL_guid_h_
#include "SDL_stdinc.h"
#include "SDL_error.h"
#include "begin_code.h"
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/**
* An SDL_GUID is a 128-bit identifier for an input device that
* identifies that device across runs of SDL programs on the same
* platform. If the device is detached and then re-attached to a
* different port, or if the base system is rebooted, the device
* should still report the same GUID.
*
* GUIDs are as precise as possible but are not guaranteed to
* distinguish physically distinct but equivalent devices. For
* example, two game controllers from the same vendor with the same
* product ID and revision may have the same GUID.
*
* GUIDs may be platform-dependent (i.e., the same device may report
* different GUIDs on different operating systems).
*/
typedef struct {
Uint8 data[16];
} SDL_GUID;
/* Function prototypes */
/**
* Get an ASCII string representation for a given ::SDL_GUID.
*
* You should supply at least 33 bytes for pszGUID.
*
* \param guid the ::SDL_GUID you wish to convert to string
* \param pszGUID buffer in which to write the ASCII string
* \param cbGUID the size of pszGUID
*
* \since This function is available since SDL 2.tbd.
*
* \sa SDL_GUIDFromString
*/
extern DECLSPEC void SDLCALL SDL_GUIDToString(SDL_GUID guid, char *pszGUID, int cbGUID);
/**
* Convert a GUID string into a ::SDL_GUID structure.
*
* Performs no error checking. If this function is given a string containing
* an invalid GUID, the function will silently succeed, but the GUID generated
* will not be useful.
*
* \param pchGUID string containing an ASCII representation of a GUID
* \returns a ::SDL_GUID structure.
*
* \since This function is available since SDL 2.tbd.
*
* \sa SDL_GUIDToString
*/
extern DECLSPEC SDL_GUID SDLCALL SDL_GUIDFromString(const char *pchGUID);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include "close_code.h"
#endif /* SDL_guid_h_ */
/* vi: set ts=4 sw=4 expandtab: */

View file

@ -43,6 +43,7 @@
#include "SDL_stdinc.h"
#include "SDL_error.h"
#include "SDL_guid.h"
#include "begin_code.h"
/* Set up for C function definitions, even when using C++ */
@ -69,9 +70,7 @@ struct _SDL_Joystick;
typedef struct _SDL_Joystick SDL_Joystick;
/* A structure that encodes the stable unique id for a joystick device */
typedef struct {
Uint8 data[16];
} SDL_JoystickGUID;
typedef SDL_GUID SDL_JoystickGUID;
/**
* This is a unique ID for a joystick for the time it is connected to the system,

91
src/SDL_guid.c Normal file
View file

@ -0,0 +1,91 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_guid.h"
/* convert the guid to a printable string */
void SDL_GUIDToString(SDL_GUID guid, char *pszGUID, int cbGUID)
{
static const char k_rgchHexToASCII[] = "0123456789abcdef";
int i;
if ((pszGUID == NULL) || (cbGUID <= 0)) {
return;
}
for (i = 0; i < sizeof(guid.data) && i < (cbGUID-1)/2; i++) {
/* each input byte writes 2 ascii chars, and might write a null byte. */
/* If we don't have room for next input byte, stop */
unsigned char c = guid.data[i];
*pszGUID++ = k_rgchHexToASCII[c >> 4];
*pszGUID++ = k_rgchHexToASCII[c & 0x0F];
}
*pszGUID = '\0';
}
/*-----------------------------------------------------------------------------
* Purpose: Returns the 4 bit nibble for a hex character
* Input : c -
* Output : unsigned char
*-----------------------------------------------------------------------------*/
static unsigned char nibble(unsigned char c)
{
if ((c >= '0') && (c <= '9')) {
return (c - '0');
}
if ((c >= 'A') && (c <= 'F')) {
return (c - 'A' + 0x0a);
}
if ((c >= 'a') && (c <= 'f')) {
return (c - 'a' + 0x0a);
}
/* received an invalid character, and no real way to return an error */
/* AssertMsg1(false, "Q_nibble invalid hex character '%c' ", c); */
return 0;
}
/* convert the string version of a guid to the struct */
SDL_GUID SDL_GUIDFromString(const char *pchGUID)
{
SDL_GUID guid;
int maxoutputbytes= sizeof(guid);
size_t len = SDL_strlen(pchGUID);
Uint8 *p;
size_t i;
/* Make sure it's even */
len = (len) & ~0x1;
SDL_memset(&guid, 0x00, sizeof(guid));
p = (Uint8 *)&guid;
for (i = 0; (i < len) && ((p - (Uint8 *)&guid) < maxoutputbytes); i+=2, p++) {
*p = (nibble((unsigned char)pchGUID[i]) << 4) | nibble((unsigned char)pchGUID[i+1]);
}
return guid;
}

View file

@ -843,3 +843,5 @@
++'_SDL_JoystickAttachVirtualEx'.'SDL2.dll'.'SDL_JoystickAttachVirtualEx'
++'_SDL_GameControllerGetFirmwareVersion'.'SDL2.dll'.'SDL_GameControllerGetFirmwareVersion'
++'_SDL_JoystickGetFirmwareVersion'.'SDL2.dll'.'SDL_JoystickGetFirmwareVersion'
++'_SDL_GUIDToString'.'SDL2.dll'.'SDL_GUIDToString'
++'_SDL_GUIDFromString'.'SDL2.dll'.'SDL_GUIDFromString'

View file

@ -869,3 +869,5 @@
#define SDL_JoystickAttachVirtualEx SDL_JoystickAttachVirtualEx_REAL
#define SDL_GameControllerGetFirmwareVersion SDL_GameControllerGetFirmwareVersion_REAL
#define SDL_JoystickGetFirmwareVersion SDL_JoystickGetFirmwareVersion_REAL
#define SDL_GUIDToString SDL_GUIDToString_REAL
#define SDL_GUIDFromString SDL_GUIDFromString_REAL

View file

@ -944,3 +944,5 @@ SDL_DYNAPI_PROC(const char*,SDL_JoystickPath,(SDL_Joystick *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_JoystickAttachVirtualEx,(const SDL_VirtualJoystickDesc *a),(a),return)
SDL_DYNAPI_PROC(Uint16,SDL_GameControllerGetFirmwareVersion,(SDL_GameController *a),(a),return)
SDL_DYNAPI_PROC(Uint16,SDL_JoystickGetFirmwareVersion,(SDL_Joystick *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_GUIDToString,(SDL_GUID a, char *b, int c),(a,b,c),)
SDL_DYNAPI_PROC(SDL_GUID,SDL_GUIDFromString,(const char *a),(a),return)

View file

@ -2735,68 +2735,13 @@ SDL_JoystickType SDL_JoystickGetType(SDL_Joystick *joystick)
/* convert the guid to a printable string */
void SDL_JoystickGetGUIDString(SDL_JoystickGUID guid, char *pszGUID, int cbGUID)
{
static const char k_rgchHexToASCII[] = "0123456789abcdef";
int i;
if ((pszGUID == NULL) || (cbGUID <= 0)) {
return;
}
for (i = 0; i < sizeof(guid.data) && i < (cbGUID-1)/2; i++) {
/* each input byte writes 2 ascii chars, and might write a null byte. */
/* If we don't have room for next input byte, stop */
unsigned char c = guid.data[i];
*pszGUID++ = k_rgchHexToASCII[c >> 4];
*pszGUID++ = k_rgchHexToASCII[c & 0x0F];
}
*pszGUID = '\0';
}
/*-----------------------------------------------------------------------------
* Purpose: Returns the 4 bit nibble for a hex character
* Input : c -
* Output : unsigned char
*-----------------------------------------------------------------------------*/
static unsigned char nibble(unsigned char c)
{
if ((c >= '0') && (c <= '9')) {
return (c - '0');
}
if ((c >= 'A') && (c <= 'F')) {
return (c - 'A' + 0x0a);
}
if ((c >= 'a') && (c <= 'f')) {
return (c - 'a' + 0x0a);
}
/* received an invalid character, and no real way to return an error */
/* AssertMsg1(false, "Q_nibble invalid hex character '%c' ", c); */
return 0;
SDL_GUIDToString(guid, pszGUID, cbGUID);
}
/* convert the string version of a joystick guid to the struct */
SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID)
{
SDL_JoystickGUID guid;
int maxoutputbytes= sizeof(guid);
size_t len = SDL_strlen(pchGUID);
Uint8 *p;
size_t i;
/* Make sure it's even */
len = (len) & ~0x1;
SDL_memset(&guid, 0x00, sizeof(guid));
p = (Uint8 *)&guid;
for (i = 0; (i < len) && ((p - (Uint8 *)&guid) < maxoutputbytes); i+=2, p++) {
*p = (nibble((unsigned char)pchGUID[i]) << 4) | nibble((unsigned char)pchGUID[i+1]);
}
return guid;
return SDL_GUIDFromString(pchGUID);
}
/* update the power level for this joystick */

View file

@ -85,6 +85,7 @@ add_executable(testgesture testgesture.c)
add_executable(testgl2 testgl2.c)
add_executable(testgles testgles.c)
add_executable(testgles2 testgles2.c)
add_executable(testguid testguid.c)
add_executable(testhaptic testhaptic.c)
add_executable(testhotplug testhotplug.c)
add_executable(testrumble testrumble.c)
@ -166,6 +167,7 @@ SET(ALL_TESTS
testgl2
testgles
testgles2
testguid
testhaptic
testhittesting
testhotplug
@ -214,6 +216,7 @@ set(NONINTERACTIVE
testatomic
testerror
testfilesystem
testguid
testlocale
testplatform
testpower
@ -313,35 +316,36 @@ if(PSP)
# Build EBOOT files if building for PSP
set(BUILD_EBOOT
${NEEDS_RESOURCES}
testbounds
testgl2
testsem
testdisplayinfo
teststreaming
testgeometry
testfile
testdraw2
testviewport
testhittesting
testoverlay2
testver
testdrawchessboard
testsurround
testintersections
testmessage
testaudiocapture
testerror
testatomic
testjoystick
testiconv
testfilesystem
testplatform
testthread
testqsort
testaudiocapture
testaudioinfo
testbounds
testdisplayinfo
testdraw2
testdrawchessboard
testerror
testfile
testfilesystem
testgeometry
testgl2
testguid
testhittesting
testiconv
testintersections
testjoystick
testlock
testtimer
testmessage
testoverlay2
testplatform
testpower
testqsort
testsem
teststreaming
testsurround
testthread
testtimer
testver
testviewport
testwm2
torturethread
)

View file

@ -40,6 +40,7 @@ TARGETS = \
testgamecontroller$(EXE) \
testgeometry$(EXE) \
testgesture$(EXE) \
testguid$(EXE) \
testhaptic$(EXE) \
testhittesting$(EXE) \
testhotplug$(EXE) \
@ -214,6 +215,9 @@ testgles2$(EXE): $(srcdir)/testgles2.c
testgles2_sdf$(EXE): $(srcdir)/testgles2_sdf.c $(srcdir)/testutils.c
$(CC) -o $@ $^ $(CFLAGS) $(LIBS) @MATHLIB@
testguid$(EXE): $(srcdir)/testguid.c
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
testhaptic$(EXE): $(srcdir)/testhaptic.c
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)

169
test/testguid.c Normal file
View file

@ -0,0 +1,169 @@
/*
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
*/
/**
* Automated tests for GUID processing
*/
#include <stdio.h>
#include "SDL.h"
/* Helpers */
static int _error_count = 0;
static int
_require_eq(Uint64 expected, Uint64 actual, int line, char *msg)
{
if (expected != actual) {
_error_count += 1;
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "[%s, L%d] %s: Actual %ld (0x%lx) != expected %ld (0x%lx)",
__FILE__, line, msg, actual, actual, expected, expected);
return 0;
}
return 1;
}
#define ASSERT_EQ(MSG, EXPECTED, ACTUAL) _require_eq((EXPECTED), (ACTUAL), __LINE__, (MSG))
/* Helpers */
#define NUM_TEST_GUIDS 5
static struct {
char *str;
Uint64 upper, lower;
} test_guids[NUM_TEST_GUIDS] = {
{ "0000000000000000" "ffffffffffffffff",
0x0000000000000000, 0xfffffffffffffffflu },
{ "0011223344556677" "8091a2b3c4d5e6f0",
0x0011223344556677lu, 0x8091a2b3c4d5e6f0lu },
{ "a011223344556677" "8091a2b3c4d5e6f0",
0xa011223344556677lu, 0x8091a2b3c4d5e6f0lu },
{ "a011223344556677" "8091a2b3c4d5e6f1",
0xa011223344556677lu, 0x8091a2b3c4d5e6f1lu },
{ "a011223344556677" "8191a2b3c4d5e6f0",
0xa011223344556677lu, 0x8191a2b3c4d5e6f0lu },
};
static void
upper_lower_to_bytestring(Uint8* out, Uint64 upper, Uint64 lower)
{
Uint64 values[2];
int i, k;
values[0] = upper;
values [1] = lower;
for (i = 0; i < 2; ++i) {
Uint64 v = values[i];
for (k = 0; k < 8; ++k) {
*out++ = v >> 56;
v <<= 8;
}
}
}
/* ================= Test Case Implementation ================== */
/**
* @brief Check String-to-GUID conversion
*
* @sa SDL_GUIDFromString
*/
static void
TestGuidFromString(void)
{
int i;
for (i = 0; i < NUM_TEST_GUIDS; ++i) {
Uint8 expected[16];
SDL_GUID guid;
upper_lower_to_bytestring(expected,
test_guids[i].upper, test_guids[i].lower);
guid = SDL_GUIDFromString(test_guids[i].str);
if (!ASSERT_EQ("GUID from string", 0, SDL_memcmp(expected, guid.data, 16))) {
SDL_Log(" GUID was: '%s'", test_guids[i].str);
}
}
}
/**
* @brief Check GUID-to-String conversion
*
* @sa SDL_GUIDToString
*/
static void
TestGuidToString(void)
{
int i;
for (i = 0; i < NUM_TEST_GUIDS; ++i) {
const int guid_str_offset = 4;
char guid_str_buf[64];
char *guid_str = guid_str_buf + guid_str_offset;
SDL_GUID guid;
int size;
upper_lower_to_bytestring(guid.data,
test_guids[i].upper, test_guids[i].lower);
/* Serialise to limited-length buffers */
for (size = 0; size <= 36; ++size) {
const Uint8 fill_char = size + 0xa0;
Uint32 expected_prefix;
Uint32 actual_prefix;
int written_size;
SDL_memset(guid_str_buf, fill_char, sizeof(guid_str_buf));
SDL_GUIDToString(guid, guid_str, size);
/* Check bytes before guid_str_buf */
expected_prefix = fill_char | (fill_char << 8) | (fill_char << 16) | (fill_char << 24);
SDL_memcpy(&actual_prefix, guid_str_buf, 4);
if (!ASSERT_EQ("String buffer memory before output untouched: ", expected_prefix, actual_prefix)) {
SDL_Log(" at size=%d", size);
}
/* Check that we did not overwrite too much */
written_size = 0;
while ((guid_str[written_size] & 0xff) != fill_char && written_size < 256) {
++written_size;
}
if (!ASSERT_EQ("Output length is within expected bounds", 1, written_size <= size)) {
SDL_Log(" with lenght %d: wrote %d of %d permitted bytes",
size, written_size, size);
}
if (size >= 33) {
if (!ASSERT_EQ("GUID string equality", 0, SDL_strcmp(guid_str, test_guids[i].str))) {
SDL_Log(" from string: %s", test_guids[i].str);
}
}
}
}
}
int
main(int argc, char *argv[])
{
/* Enable standard application logging */
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
TestGuidFromString();
TestGuidToString();
return _error_count > 0;
}

View file

@ -11,7 +11,7 @@ TARGETS = testatomic.exe testdisplayinfo.exe testbounds.exe testdraw2.exe &
testdrawchessboard.exe testdropfile.exe testerror.exe testfile.exe &
testfilesystem.exe testgamecontroller.exe testgeometry.exe testgesture.exe &
testhittesting.exe testhotplug.exe testiconv.exe testime.exe testlocale.exe &
testintersections.exe testjoystick.exe testkeys.exe testloadso.exe &
testguid.exe testintersections.exe testjoystick.exe testkeys.exe testloadso.exe &
testlock.exe testmessage.exe testoverlay2.exe testplatform.exe &
testpower.exe testsensor.exe testrelative.exe testrendercopyex.exe &
testrendertarget.exe testrumble.exe testscale.exe testsem.exe &