diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5073f2310..393f50905 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,8 @@ endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ON ) +option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON ) + if( CMAKE_SIZEOF_VOID_P MATCHES "8" ) set( X64 64 ) endif( CMAKE_SIZEOF_VOID_P MATCHES "8" ) @@ -213,7 +215,9 @@ else( WIN32 ) if( NOT SDL_FOUND ) message( SEND_ERROR "SDL is required for building." ) endif( NOT SDL_FOUND ) - set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SDL_LIBRARY}" ) + if( NOT APPLE OR NOT OSX_COCOA_BACKEND ) + set( ZDOOM_LIBS ${ZDOOM_LIBS} "${SDL_LIBRARY}" ) + endif( NOT APPLE OR NOT OSX_COCOA_BACKEND ) include_directories( "${SDL_INCLUDE_DIR}" ) find_path( FPU_CONTROL_DIR fpu_control.h ) @@ -375,19 +379,19 @@ if( NOT NO_ASM ) set( ASM_FLAGS -f win32 -DWIN32 -i${CMAKE_CURRENT_SOURCE_DIR}/ ) endif( X64 ) endif( UNIX ) - if( WIN32 ) + if( WIN32 AND NOT X64 ) set( FIXRTEXT fixrtext ) - else( WIN32 ) + else( WIN32 AND NOT X64 ) set( FIXRTEXT "" ) - endif( WIN32 ) + endif( WIN32 AND NOT X64 ) message( STATUS "Selected assembler: ${ASSEMBLER}" ) MACRO( ADD_ASM_FILE indir infile ) set( ASM_OUTPUT_${infile} "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/zdoom.dir/${indir}/${infile}${ASM_OUTPUT_EXTENSION}" ) - if( WIN32 ) + if( WIN32 AND NOT X64 ) set( FIXRTEXT_${infile} COMMAND ${FIXRTEXT} "${ASM_OUTPUT_${infile}}" ) - else( WIN32 ) + else( WIN32 AND NOT X64 ) set( FIXRTEXT_${infile} COMMAND "" ) - endif( WIN32 ) + endif( WIN32 AND NOT X64 ) add_custom_command( OUTPUT ${ASM_OUTPUT_${infile}} COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/zdoom.dir/${indir} COMMAND ${ASSEMBLER} ${ASM_FLAGS} -o"${ASM_OUTPUT_${infile}}" "${CMAKE_CURRENT_SOURCE_DIR}/${indir}/${infile}${ASM_SOURCE_EXTENSION}" @@ -446,9 +450,10 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) endif( PROFILE ) set( REL_CXX_FLAGS "-fno-rtti" ) - if( NOT PROFILE ) + if( NOT PROFILE AND NOT APPLE ) + # On OS X frame pointers are required for exception handling, at least with Clang set( REL_CXX_FLAGS "${REL_CXX_FLAGS} -fomit-frame-pointer" ) - endif( NOT PROFILE ) + endif( NOT PROFILE AND NOT APPLE ) set( CMAKE_CXX_FLAGS_RELEASE "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}" ) set( CMAKE_CXX_FLAGS_MINSIZEREL "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_MINSIZEREL}" ) set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" ) @@ -579,22 +584,53 @@ set( PLAT_WIN32_SOURCES win32/st_start.cpp win32/win32gliface.cpp win32/win32video.cpp ) -set( PLAT_SDL_SOURCES +set( PLAT_SDL_SYSTEM_SOURCES sdl/crashcatcher.c sdl/hardware.cpp sdl/i_cd.cpp - sdl/i_input.cpp - sdl/i_joystick.cpp sdl/i_main.cpp sdl/i_movie.cpp sdl/i_system.cpp sdl/sdlvideo.cpp sdl/sdlglvideo.cpp sdl/st_start.cpp ) +set( PLAT_SDL_SPECIAL_SOURCES + sdl/i_gui.cpp + sdl/i_input.cpp + sdl/i_joystick.cpp + sdl/i_timer.cpp ) set( PLAT_MAC_SOURCES - sdl/SDLMain.m sdl/iwadpicker_cocoa.mm sdl/i_system_cocoa.mm ) +set( PLAT_COCOA_SOURCES + cocoa/HID_Config_Utilities.c + cocoa/HID_Error_Handler.c + cocoa/HID_Name_Lookup.c + cocoa/HID_Queue_Utilities.c + cocoa/HID_Utilities.c + cocoa/IOHIDDevice_.c + cocoa/IOHIDElement_.c + cocoa/ImmrHIDUtilAddOn.c + cocoa/i_backend_cocoa.mm + cocoa/i_joystick.cpp + cocoa/i_timer.cpp + cocoa/zdoom.icns ) + +if( APPLE ) + set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} "${FMOD_LIBRARY}" ) + + if( OSX_COCOA_BACKEND ) + set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} ${PLAT_COCOA_SOURCES} ) + else( OSX_COCOA_BACKEND ) + set( PLAT_MAC_SOURCES ${PLAT_MAC_SOURCES} ${PLAT_SDL_SPECIAL_SOURCES} sdl/SDLMain.m ) + endif( OSX_COCOA_BACKEND ) + + set_source_files_properties( cocoa/zdoom.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) + set_source_files_properties( "${FMOD_LIBRARY}" PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks ) +else( APPLE ) + set( PLAT_SDL_SOURCES ${PLAT_SDL_SYSTEM_SOURCES} ${PLAT_SDL_SPECIAL_SOURCES} ) +endif( APPLE ) + if( WIN32 ) set( SYSTEM_SOURCES_DIR win32 ) set( SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ) @@ -810,7 +846,7 @@ set( NOT_COMPILED_SOURCE_FILES asm_x86_64/tmap3.s ) -add_executable( zdoom WIN32 +add_executable( zdoom WIN32 MACOSX_BUNDLE ${HEADER_FILES} ${NOT_COMPILED_SOURCE_FILES} __autostart.cpp @@ -1087,6 +1123,7 @@ add_executable( zdoom WIN32 oplsynth/opl_mus_player.cpp oplsynth/dosbox/opl.cpp oplsynth/OPL3.cpp + oplsynth/nukedopl3.cpp resourcefiles/ancientzip.cpp resourcefiles/file_7z.cpp resourcefiles/file_grp.cpp @@ -1259,6 +1296,25 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) endif( SSE_MATTERS ) endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) +if( APPLE ) + set_target_properties(zdoom PROPERTIES + LINK_FLAGS "-framework Carbon -framework Cocoa -framework IOKit -framework OpenGL" + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cocoa/zdoom-info.plist" ) + + # Fix fmod link so that it can be found in the app bundle. + find_program( OTOOL otool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) + find_program( INSTALL_NAME_TOOL install_name_tool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) + execute_process( COMMAND "${OTOOL}" -L "${FMOD_LIBRARY}" + COMMAND grep "libfmodex.dylib (compat" + COMMAND head -n1 + COMMAND awk "{print $1}" + OUTPUT_VARIABLE FMOD_LINK + OUTPUT_STRIP_TRAILING_WHITESPACE ) + add_custom_command( TARGET zdoom POST_BUILD + COMMAND "${INSTALL_NAME_TOOL}" -change "${FMOD_LINK}" @executable_path/../Frameworks/libfmodex.dylib "$" + COMMENT "Relinking FMOD Ex" ) +endif( APPLE ) + source_group("Assembly Files\\ia32" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/asm_ia32/.+") source_group("Assembly Files\\x86_64" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/asm_x86_64/.+") source_group("Audio Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/.+") @@ -1266,6 +1322,7 @@ source_group("Audio Files\\OPL Synth" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURC source_group("Audio Files\\OPL Synth\\DOSBox" FILES oplsynth/dosbox/opl.cpp oplsynth/dosbox/opl.h) source_group("Audio Files\\Timidity\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.h$") source_group("Audio Files\\Timidity\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.cpp$") +source_group("Cocoa Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/cocoa/.+") source_group("Decorate++" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/thingdef/.+") source_group("FraggleScript" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/fragglescript/.+") source_group("Games\\Doom Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_doom/.+") diff --git a/src/actor.h b/src/actor.h index 9452ed5e4..e80cdb227 100644 --- a/src/actor.h +++ b/src/actor.h @@ -971,6 +971,7 @@ public: FNameNoInit DamageType; FNameNoInit DamageTypeReceived; fixed_t DamageFactor; + fixed_t DamageMultiply; FNameNoInit PainType; FNameNoInit DeathType; diff --git a/src/b_bot.cpp b/src/b_bot.cpp index dc6f2cdc4..d2acdcab6 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -24,12 +24,14 @@ IMPLEMENT_POINTY_CLASS(DBot) END_POINTERS DBot::DBot () +: DThinker(STAT_BOT) { Clear (); } void DBot::Clear () { + player = NULL; angle = 0; dest = NULL; prev = NULL; @@ -64,6 +66,10 @@ void DBot::Serialize (FArchive &arc) arc << savedyaw << savedpitch; } + else if (SaveVersion >= 4516) + { + arc << player; + } arc << angle << dest @@ -88,6 +94,22 @@ void DBot::Serialize (FArchive &arc) << oldy; } +void DBot::Tick () +{ + Super::Tick (); + + if (player->mo == NULL || bglobal.freeze) + { + return; + } + + BotThinkCycles.Clock(); + bglobal.m_Thinking = true; + bglobal.Think (player->mo, &netcmds[player - players][((gametic + 1)/ticdup)%BACKUPTICS]); + bglobal.m_Thinking = false; + BotThinkCycles.Unclock(); +} + CVAR (Int, bot_next_color, 11, 0) CVAR (Bool, bot_observer, false, 0) diff --git a/src/b_bot.h b/src/b_bot.h index 1862faee5..7740e5e00 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -14,6 +14,7 @@ #include "d_ticcmd.h" #include "r_defs.h" #include "a_pickups.h" +#include "stats.h" #define FORWARDWALK 0x1900 #define FORWARDRUN 0x3200 @@ -89,20 +90,22 @@ public: void ClearPlayer (int playernum, bool keepTeam); //(B_Game.c) - void Main (int buf); + void Main (); void Init (); void End(); bool SpawnBot (const char *name, int color = NOCOLOR); - bool LoadBots (); - void ForgetBots (); void TryAddBot (BYTE **stream, int player); void RemoveAllBots (bool fromlist); - void DestroyAllBots (); + bool LoadBots (); + void ForgetBots (); //(B_Func.c) bool Check_LOS (AActor *mobj1, AActor *mobj2, angle_t vangle); + void StartTravel (); + void FinishTravel (); //(B_Think.c) + void Think (AActor *actor, ticcmd_t *cmd); void WhatToGet (AActor *actor, AActor *item); //(B_move.c) @@ -144,7 +147,6 @@ private: bool SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FCheckPosition &tm); //(B_Think.c) - void Think (AActor *actor, ticcmd_t *cmd); void ThinkForMove (AActor *actor, ticcmd_t *cmd); void Set_enemy (AActor *actor); @@ -155,16 +157,18 @@ protected: bool observer; //Consoleplayer is observer. }; -class DBot : public DObject +class DBot : public DThinker { - DECLARE_CLASS(DBot,DObject) + DECLARE_CLASS(DBot,DThinker) HAS_OBJECT_POINTERS public: DBot (); void Clear (); void Serialize (FArchive &arc); + void Tick (); + player_t *player; angle_t angle; // The wanted angle that the bot try to get every tic. // (used to get a smooth view movement) TObjPtr dest; // Move Destination. @@ -205,6 +209,7 @@ public: //Externs extern FCajunMaster bglobal; +extern cycle_t BotThinkCycles, BotSupportCycles; EXTERN_CVAR (Float, bot_flag_return_time) EXTERN_CVAR (Int, bot_next_color) diff --git a/src/b_func.cpp b/src/b_func.cpp index 0303de8c6..349155a81 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -402,7 +402,7 @@ AActor *FCajunMaster::Find_enemy (AActor *bot) && bot != client->mo) { if (Check_LOS (bot, client->mo, vangle)) //Here's a strange one, when bot is standing still, the P_CheckSight within Check_LOS almost always returns false. tought it should be the same checksight as below but.. (below works) something must be fuckin wierd screded up. - //if(P_CheckSight( bot, players[count].mo)) + //if(P_CheckSight(bot, players[count].mo)) { temp = P_AproxDistance (client->mo->x - bot->x, client->mo->y - bot->y); @@ -552,3 +552,25 @@ bool FCajunMaster::SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FChec actor->flags = savedFlags; return res; } + +void FCajunMaster::StartTravel () +{ + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (players[i].Bot != NULL) + { + players[i].Bot->ChangeStatNum (STAT_TRAVELLING); + } + } +} + +void FCajunMaster::FinishTravel () +{ + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (players[i].Bot != NULL) + { + players[i].Bot->ChangeStatNum (STAT_BOT); + } + } +} diff --git a/src/b_game.cpp b/src/b_game.cpp index 853ba7f23..44aff03fa 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -94,71 +94,46 @@ FCajunMaster::~FCajunMaster() ForgetBots(); } -//This function is called every tick (from g_game.c), -//send bots into thinking (+more). -void FCajunMaster::Main (int buf) +//This function is called every tick (from g_game.c). +void FCajunMaster::Main () { - int i; - BotThinkCycles.Reset(); - if (demoplayback) + if (demoplayback || gamestate != GS_LEVEL || consoleplayer != Net_Arbitrator) return; - if (gamestate != GS_LEVEL) - return; - - m_Thinking = true; - - //Think for bots. - if (botnum) + //Add new bots? + if (wanted_botnum > botnum && !freeze) { - BotThinkCycles.Clock(); - for (i = 0; i < MAXPLAYERS; i++) + if (t_join == ((wanted_botnum - botnum) * SPAWN_DELAY)) { - if (playeringame[i] && players[i].mo && !freeze && players[i].Bot != NULL) - Think (players[i].mo, &netcmds[i][buf]); + if (!SpawnBot (getspawned[spawn_tries])) + wanted_botnum--; + spawn_tries++; } - BotThinkCycles.Unclock(); + + t_join--; } - if (consoleplayer == Net_Arbitrator) + //Check if player should go observer. Or un observe + if (bot_observer && !observer && !netgame) { - //Add new bots? - if (wanted_botnum > botnum && !freeze) - { - if (t_join == ((wanted_botnum - botnum) * SPAWN_DELAY)) - { - if (!SpawnBot (getspawned[spawn_tries])) - wanted_botnum--; - spawn_tries++; - } - - t_join--; - } - - //Check if player should go observer. Or un observe - if (bot_observer && !observer && !netgame) - { - Printf ("%s is now observer\n", players[consoleplayer].userinfo.GetName()); - observer = true; - players[consoleplayer].mo->UnlinkFromWorld (); - players[consoleplayer].mo->flags = MF_DROPOFF|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOTDMATCH|MF_NOGRAVITY|MF_FRIENDLY; - players[consoleplayer].mo->flags2 |= MF2_FLY; - players[consoleplayer].mo->LinkToWorld (); - } - else if (!bot_observer && observer && !netgame) //Go back - { - Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.GetName()); - observer = false; - players[consoleplayer].mo->UnlinkFromWorld (); - players[consoleplayer].mo->flags = MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH|MF_FRIENDLY; - players[consoleplayer].mo->flags2 &= ~MF2_FLY; - players[consoleplayer].mo->LinkToWorld (); - } + Printf ("%s is now observer\n", players[consoleplayer].userinfo.GetName()); + observer = true; + players[consoleplayer].mo->UnlinkFromWorld (); + players[consoleplayer].mo->flags = MF_DROPOFF|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOTDMATCH|MF_NOGRAVITY|MF_FRIENDLY; + players[consoleplayer].mo->flags2 |= MF2_FLY; + players[consoleplayer].mo->LinkToWorld (); + } + else if (!bot_observer && observer && !netgame) //Go back + { + Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.GetName()); + observer = false; + players[consoleplayer].mo->UnlinkFromWorld (); + players[consoleplayer].mo->flags = MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH|MF_FRIENDLY; + players[consoleplayer].mo->flags2 &= ~MF2_FLY; + players[consoleplayer].mo->LinkToWorld (); } - - m_Thinking = false; } void FCajunMaster::Init () @@ -199,18 +174,13 @@ void FCajunMaster::End () //Arrange wanted botnum and their names, so they can be spawned next level. getspawned.Clear(); - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].Bot != NULL) - { - if (deathmatch) - { - getspawned.Push(players[i].userinfo.GetName()); - } - } - } if (deathmatch) { + for (i = 0; i < MAXPLAYERS; i++) + { + getspawned.Push(players[i].userinfo.GetName()); + } + wanted_botnum = botnum; } } @@ -400,7 +370,7 @@ bool FCajunMaster::DoAddBot (BYTE *info, botskill_t skill) multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost). players[bnum].Bot = new DBot; - GC::WriteBarrier (players[bnum].Bot); + players[bnum].Bot->player = &players[bnum]; players[bnum].Bot->skill = skill; playeringame[bnum] = true; players[bnum].mo = NULL; @@ -426,7 +396,7 @@ void FCajunMaster::RemoveAllBots (bool fromlist) for (i = 0; i < MAXPLAYERS; ++i) { - if (playeringame[i] && players[i].Bot != NULL) + if (players[i].Bot != NULL) { // If a player is looking through this bot's eyes, make him // look through his own eyes instead. @@ -456,18 +426,6 @@ void FCajunMaster::RemoveAllBots (bool fromlist) botnum = 0; } -void FCajunMaster::DestroyAllBots () -{ - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (players[i].Bot != NULL) - { - players[i].Bot->Destroy (); - players[i].Bot = NULL; - } - } -} - //------------------ //Reads data for bot from diff --git a/src/c_bind.cpp b/src/c_bind.cpp index 0744efeee..66ed14ca5 100644 --- a/src/c_bind.cpp +++ b/src/c_bind.cpp @@ -230,7 +230,11 @@ const char *KeyNames[NUM_KEYS] = NULL, NULL, NULL, NULL, NULL, "pause", NULL, "home", //C0 "uparrow", "pgup", NULL, "leftarrow",NULL, "rightarrow",NULL, "end", //C8 "downarrow","pgdn", "ins", "del", NULL, NULL, NULL, NULL, //D0 +#ifdef __APPLE__ + NULL, NULL, NULL, "command", NULL, "apps", "power", "sleep", //D8 +#else // !__APPLE__ NULL, NULL, NULL, "lwin", "rwin", "apps", "power", "sleep", //D8 +#endif // __APPLE__ NULL, NULL, NULL, "wake", NULL, "search", "favorites","refresh", //E0 "webstop", "webforward","webback", "mycomputer","mail", "mediaselect",NULL, NULL, //E8 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //F0 diff --git a/src/c_console.cpp b/src/c_console.cpp index 9dcaa0820..c6fe58749 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1426,7 +1426,11 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len) case 'V': TabbedLast = false; TabbedList = false; +#ifdef __APPLE__ + if (ev->data3 & GKM_META) +#else // !__APPLE__ if (ev->data3 & GKM_CTRL) +#endif // __APPLE__ { if (data1 == 'C') { // copy to clipboard diff --git a/src/cocoa/HID_Config_Utilities.c b/src/cocoa/HID_Config_Utilities.c new file mode 100644 index 000000000..18d4672d3 --- /dev/null +++ b/src/cocoa/HID_Config_Utilities.c @@ -0,0 +1,926 @@ +// File: HID_Config_Utilities.c +// Abstract: Implementation of the HID configuration utilities +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + +#define LOG_SCORING 0 + +#include // malloc +#include // clock + +#include + +#include "HID_Utilities_External.h" + +// --------------------------------- + +// polls single device's elements for a change greater than kPercentMove. Times out after given time +// returns 1 and pointer to element if found +// returns 0 and NULL for both parameters if not found + +unsigned char HIDConfigureSingleDeviceAction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float timeout) { + if ( !inIOHIDDeviceRef ) { + return (0); + } + if ( 0 == HIDHaveDeviceList() ) { // if we do not have a device list + return (0); // return 0 + } + + Boolean found = FALSE; + + // build list of device and elements to save current values + int maxElements = HIDCountDeviceElements(inIOHIDDeviceRef, kHIDElementTypeInput); + int *saveValueArray = (int *) calloc( maxElements, sizeof(int) ); // 2D array to save values + + // store initial values on first pass / compare to initial value on subsequent passes + Boolean first = TRUE; + + // get all the elements from this device + CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); + // if that worked... + if ( elementCFArrayRef ) { + clock_t start = clock(), end; + + // poll all devices and elements + while ( !found ) { + int currElementIndex = 0; + CFIndex idx, cnt = CFArrayGetCount(elementCFArrayRef); + for ( idx = 0; idx < cnt; idx++ ) { + *outIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); + if ( !*outIOHIDElementRef ) { + continue; + } + + // is this an input element? + IOHIDElementType type = IOHIDElementGetType(*outIOHIDElementRef); + + switch ( type ) { + // these types are inputs + case kIOHIDElementTypeInput_Misc: + case kIOHIDElementTypeInput_Button: + case kIOHIDElementTypeInput_Axis: + case kIOHIDElementTypeInput_ScanCodes: + default: + { + break; + } + case kIOHIDElementTypeOutput: + case kIOHIDElementTypeFeature: + case kIOHIDElementTypeCollection: + { + *outIOHIDElementRef = NULL; // these types are not ( Skip them ) + break; + } + } /* switch */ + if ( !*outIOHIDElementRef ) { + continue; // skip this element + } + + // get this elements current value + int value = 0; // default value is zero + IOHIDValueRef tIOHIDValueRef; + IOReturn ioReturn = IOHIDDeviceGetValue(inIOHIDDeviceRef, *outIOHIDElementRef, &tIOHIDValueRef); + if ( kIOReturnSuccess == ioReturn ) { + value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical); + } + if ( first ) { + saveValueArray[currElementIndex] = value; + } else { + CFIndex min = IOHIDElementGetLogicalMin(*outIOHIDElementRef); + CFIndex max = IOHIDElementGetLogicalMax(*outIOHIDElementRef); + + int initialValue = saveValueArray[currElementIndex]; + int delta = (float)(max - min) * kPercentMove * 0.01; + // is the new value within +/- delta of the initial value? + if ( ( (initialValue + delta) < value ) || ( (initialValue - delta) > value ) ) { + found = 1; // ( yes! ) mark as found + break; + } + } // if ( first ) + + currElementIndex++; // bump element index + } // next idx + + first = FALSE; // no longer the first pass + + // are we done? + end = clock(); + double secs = (double)(end - start) / CLOCKS_PER_SEC; + if ( secs > timeout ) { + break; // ( yes ) timeout + } + } // while ( !found ) + + CFRelease(elementCFArrayRef); + } // if ( elementCFArrayRef ) + // return device and element moved + if ( found ) { + return (1); + } else { + *outIOHIDElementRef = NULL; + return (0); + } +} // HIDConfigureSingleDeviceAction + +//************************************************************************* +// +// HIDConfigureAction( outIOHIDDeviceRef, outIOHIDElementRef, inTimeout ) +// +// Purpose: polls all devices and elements for a change greater than kPercentMove. +// Times out after given time returns 1 and pointer to device and element +// if found; returns 0 and NULL for both parameters if not found +// +// Inputs: outIOHIDDeviceRef - address where to store the device +// outIOHIDElementRef - address where to store the element +// inTimeout - the timeout +// Returns: Boolean - if successful +// outIOHIDDeviceRef - the device +// outIOHIDElementRef - the element +// + +Boolean HIDConfigureAction(IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float inTimeout) { + // param error? + if ( !outIOHIDDeviceRef || !outIOHIDElementRef ) { + return (0); + } + if ( !gDeviceCFArrayRef ) { // if we do not have a device list + // and we can't build another list + if ( !HIDBuildDeviceList(0, 0) || !gDeviceCFArrayRef ) { + return (FALSE); // bail + } + } + + IOHIDDeviceRef tIOHIDDeviceRef; + IOHIDElementRef tIOHIDElementRef; + + // remember when we start; used to calculate timeout + clock_t start = clock(), end; + + // determine the maximum number of elements + CFIndex maxElements = 0; + CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef); + for ( devIndex = 0; devIndex < devCount; devIndex++ ) { + tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); + if ( !tIOHIDDeviceRef ) { + continue; // skip this one + } + + CFIndex count = HIDCountDeviceElements(tIOHIDDeviceRef, kHIDElementTypeInput); + if ( count > maxElements ) { + maxElements = count; + } + } + + // allocate an array of int's in which to store devCount * maxElements values + double *saveValueArray = (double *) calloc( devCount * maxElements, sizeof(double) ); // clear 2D array to save values + + // on first pass store initial values / compare current values to initial values on subsequent passes + Boolean found = FALSE, first = TRUE; + + while ( !found ) { + double maxDeltaPercent = 0; // we want to find the one that moves the most ( percentage wise ) + for ( devIndex = 0; devIndex < devCount; devIndex++ ) { + tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); + if ( !tIOHIDDeviceRef ) { + continue; // skip this one + } + +#ifdef DEBUG + long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); + long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); +#endif + gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); + if ( gElementCFArrayRef ) { + CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef); + for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) { + tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex); + if ( !tIOHIDElementRef ) { + continue; + } + + IOHIDElementType tIOHIDElementType = IOHIDElementGetType(tIOHIDElementRef); + // only care about inputs (no outputs or features) + if ( tIOHIDElementType <= kIOHIDElementTypeInput_ScanCodes ) { + if ( IOHIDElementIsArray(tIOHIDElementRef) ) { + //printf( "ARRAY!\n" ); + continue; // skip array elements + } + + uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); + uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); + uint32_t reportCount = IOHIDElementGetReportCount(tIOHIDElementRef); +#ifdef DEBUG + if ( first ) { + IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); + printf("%s, dev: {ref:%p, ven: 0x%08lX, pro: 0x%08lX}, ele: {ref:%p, cookie: %p, usage:%04lX:%08lX}\n", + __PRETTY_FUNCTION__, + tIOHIDDeviceRef, + vendorID, + productID, + tIOHIDElementRef, + cookie, + (long unsigned int) usagePage, + (long unsigned int) usage); + fflush(stdout); + if ( (0x054C == vendorID) && (0x0268 == productID) && (0x001E == (UInt32) cookie) ) { + //printf( "DING!\n" ); + } + } + +#endif +#if 1 // work-around for IOHIDValueGetScaledValue crash (when element report count > 1) + if ( reportCount > 1 ) { + //printf( "REPORT!\n" ); + continue; // skip reports + } + +#endif + // ignore PID elements and arrays + if ( (kHIDPage_PID != usagePage) && (((uint32_t)-1) != usage) ) { + // get this elements current value + double value = 0.0; // default value is zero + IOHIDValueRef tIOHIDValueRef; + IOReturn ioReturn = IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &tIOHIDValueRef); + if ( kIOReturnSuccess == ioReturn ) { + value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical); + } + if ( first ) { + saveValueArray[(devIndex * maxElements) + eleIndex] = value; + } else { + double initialValue = saveValueArray[(devIndex * maxElements) + eleIndex]; + + CFIndex valueMin = IOHIDElementGetPhysicalMin(tIOHIDElementRef); + CFIndex valueMax = IOHIDElementGetPhysicalMax(tIOHIDElementRef); + + double deltaPercent = fabs( (initialValue - value) * 100.0 / (valueMax - valueMin) ); +#if 0 // debug code useful to dump out value info for specific (vendorID, productID, usagePage and usage) device + if ( !first ) { + // Device: 0x13b6a0 = { Logitech Inc. - WingMan Force 3D, vendorID: 0x046D, productID: 0xC283, + // usage: 0x0001:0x0004, "Generic Desktop Joystick" + if ( (vendorID == 0x046D) && (productID == 0xC283) ) { + if ( (kHIDPage_GenericDesktop == usagePage) && (kHIDUsage_GD_Rz == usage) ) { + printf("initial: %6.2f, value: %6.2f, diff: %6.2f, delta percent: %6.2f!\n", + initialValue, + value, + fabs(initialValue - value), + deltaPercent); + } + } + } + + deltaPercent = 0.0; +#endif + if ( deltaPercent >= kPercentMove ) { + found = TRUE; + if ( deltaPercent > maxDeltaPercent ) { + maxDeltaPercent = deltaPercent; + + *outIOHIDDeviceRef = tIOHIDDeviceRef; + *outIOHIDElementRef = tIOHIDElementRef; + } + + break; + } + } // if first + + } // if usage + + } // if type + + } // for elements... + + CFRelease(gElementCFArrayRef); + gElementCFArrayRef = NULL; + } // if ( gElementCFArrayRef ) + if ( found ) { + // HIDDumpElementInfo( tIOHIDElementRef ); + break; // DONE! + } + } // for devices + + first = FALSE; // no longer the first pass + + // are we done? + end = clock(); + double secs = (double)(end - start) / CLOCKS_PER_SEC; + if ( secs > inTimeout ) { + break; // ( yes ) timeout + } + } // while ( !found ) + // return device and element moved + if ( !found ) { + *outIOHIDDeviceRef = NULL; + *outIOHIDElementRef = NULL; + } + + return (found); +} // HIDConfigureAction + +//************************************************************************* +// +// HIDSaveElementPref( inKeyCFStringRef, inAppCFStringRef, inIOHIDDeviceRef, inIOHIDElementRef ) +// +// Purpose: Save the device & element values into the specified key in the specified applications preferences +// +// Inputs: inKeyCFStringRef - the preference key +// inAppCFStringRef - the application identifier +// inIOHIDDeviceRef - the device +// inIOHIDElementRef - the element +// Returns: Boolean - if successful +// + +Boolean HIDSaveElementPref(const CFStringRef inKeyCFStringRef, + CFStringRef inAppCFStringRef, + IOHIDDeviceRef inIOHIDDeviceRef, + IOHIDElementRef inIOHIDElementRef) { + Boolean success = FALSE; + if ( inKeyCFStringRef && inAppCFStringRef && inIOHIDDeviceRef && inIOHIDElementRef ) { + long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); + require(vendorID, Oops); + + long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); + require(productID, Oops); + + long locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); + require(locID, Oops); + + uint32_t usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); + uint32_t usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); + if ( !usagePage || !usage ) { + usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef); + usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef); + } + + require(usagePage && usage, Oops); + + uint32_t usagePageE = IOHIDElementGetUsagePage(inIOHIDElementRef); + uint32_t usageE = IOHIDElementGetUsage(inIOHIDElementRef); + IOHIDElementCookie eleCookie = IOHIDElementGetCookie(inIOHIDElementRef); + + CFStringRef prefCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("d:{v:%ld, p:%ld, l:%ld, p:%ld, u:%ld}, e:{p:%ld, u:%ld, c:%ld}"), + vendorID, productID, locID, usagePage, usage, + usagePageE, usageE, eleCookie); + if ( prefCFStringRef ) { + CFPreferencesSetAppValue(inKeyCFStringRef, prefCFStringRef, inAppCFStringRef); + CFRelease(prefCFStringRef); + success = TRUE; + } + } + +Oops: ; + return (success); +} // HIDSaveElementPref + +//************************************************************************* +// +// HIDRestoreElementPref( inKeyCFStringRef, inAppCFStringRef, outIOHIDDeviceRef, outIOHIDElementRef ) +// +// Purpose: Find the specified preference in the specified application +// +// Inputs: inKeyCFStringRef - the preference key +// inAppCFStringRef - the application identifier +// outIOHIDDeviceRef - address where to restore the device +// outIOHIDElementRef - address where to restore the element +// Returns: Boolean - if successful +// outIOHIDDeviceRef - the device +// outIOHIDElementRef - the element +// + +Boolean HIDRestoreElementPref(CFStringRef inKeyCFStringRef, + CFStringRef inAppCFStringRef, + IOHIDDeviceRef * outIOHIDDeviceRef, + IOHIDElementRef *outIOHIDElementRef) { + Boolean found = FALSE; + if ( inKeyCFStringRef && inAppCFStringRef && outIOHIDDeviceRef && outIOHIDElementRef ) { + CFPropertyListRef prefCFPropertyListRef = CFPreferencesCopyAppValue(inKeyCFStringRef, inAppCFStringRef); + if ( prefCFPropertyListRef ) { + if ( CFStringGetTypeID() == CFGetTypeID(prefCFPropertyListRef) ) { + char buffer[256]; + if ( CFStringGetCString( (CFStringRef) prefCFPropertyListRef, buffer, sizeof(buffer), + kCFStringEncodingUTF8 ) ) + { + HID_info_rec searchHIDInfo; + + int count = sscanf(buffer, + "d:{v:%d, p:%d, l:%d, p:%d, u:%d}, e:{p:%d, u:%d, c:%ld}", + &searchHIDInfo.device.vendorID, + &searchHIDInfo.device.productID, + &searchHIDInfo.device.locID, + &searchHIDInfo.device.usagePage, + &searchHIDInfo.device.usage, + &searchHIDInfo.element.usagePage, + &searchHIDInfo.element.usage, + (long *) &searchHIDInfo.element.cookie); + if ( 8 == count ) { // if we found all eight parametersā€¦ + // and can find a device & element that matches theseā€¦ + if ( HIDFindDeviceAndElement(&searchHIDInfo, outIOHIDDeviceRef, outIOHIDElementRef) ) { + found = TRUE; + } + } + } + } else { + // We found the entry with this key but it's the wrong type; delete it. + CFPreferencesSetAppValue(inKeyCFStringRef, NULL, inAppCFStringRef); + (void) CFPreferencesAppSynchronize(inAppCFStringRef); + } + + CFRelease(prefCFPropertyListRef); + } + } + + return (found); +} // HIDRestoreElementPref + +//************************************************************************* +// +// HIDFindDeviceAndElement( inSearchInfo, outFoundDevice, outFoundElement ) +// +// Purpose: find the closest matching device and element for this action +// +// Notes: matches device: serial, vendorID, productID, location, inUsagePage, usage +// matches element: cookie, inUsagePage, usage, +// +// Inputs: inSearchInfo - the device & element info we searching for +// outFoundDevice - the address of the best matching device +// outFoundElement - the address of the best matching element +// +// Returns: Boolean - TRUE if we find a match +// outFoundDevice - the best matching device +// outFoundElement - the best matching element +// + +Boolean HIDFindDeviceAndElement(const HID_info_rec *inSearchInfo, IOHIDDeviceRef *outFoundDevice, IOHIDElementRef *outFoundElement) { + Boolean result = FALSE; + + IOHIDDeviceRef bestIOHIDDeviceRef = NULL; + IOHIDElementRef bestIOHIDElementRef = NULL; + long bestScore = 0; + + CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef); + for ( devIndex = 0; devIndex < devCount; devIndex++ ) { + long deviceScore = 1; + + IOHIDDeviceRef tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); + if ( !tIOHIDDeviceRef ) { + continue; + } + // match vendorID, productID (+10, +8) + if ( inSearchInfo->device.vendorID ) { + long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); + if ( vendorID ) { + if ( inSearchInfo->device.vendorID == vendorID ) { + deviceScore += 10; + if ( inSearchInfo->device.productID ) { + long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); + if ( productID ) { + if ( inSearchInfo->device.productID == productID ) { + deviceScore += 8; + } // if ( inSearchInfo->device.productID == productID ) + + } // if ( productID ) + + } // if ( inSearchInfo->device.productID ) + + } // if (inSearchInfo->device.vendorID == vendorID) + + } // if vendorID + + } // if search->device.vendorID + // match usagePage & usage (+9) + if ( inSearchInfo->device.usagePage && inSearchInfo->device.usage ) { + uint32_t usagePage = IOHIDDevice_GetUsagePage(tIOHIDDeviceRef) ; + uint32_t usage = IOHIDDevice_GetUsage(tIOHIDDeviceRef); + if ( !usagePage || !usage ) { + usagePage = IOHIDDevice_GetPrimaryUsagePage(tIOHIDDeviceRef); + usage = IOHIDDevice_GetPrimaryUsage(tIOHIDDeviceRef); + } + if ( usagePage ) { + if ( inSearchInfo->device.usagePage == usagePage ) { + if ( usage ) { + if ( inSearchInfo->device.usage == usage ) { + deviceScore += 9; + } // if ( inSearchInfo->usage == usage ) + + } // if ( usage ) + + } // if ( inSearchInfo->usagePage == usagePage ) + + } // if ( usagePage ) + + } // if ( inSearchInfo->usagePage && inSearchInfo->usage ) + // match location ID (+5) + if ( inSearchInfo->device.locID ) { + long locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); + if ( locID ) { + if ( inSearchInfo->device.locID == locID ) { + deviceScore += 5; + } + } + } + + // iterate over all elements of this device + gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, 0); + if ( gElementCFArrayRef ) { + CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef); + for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) { + IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex); + if ( !tIOHIDElementRef ) { + continue; + } + + long score = deviceScore; + // match usage page, usage & cookie + if ( inSearchInfo->element.usagePage && inSearchInfo->element.usage ) { + uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); + if ( inSearchInfo->element.usagePage == usagePage ) { + uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); + if ( inSearchInfo->element.usage == usage ) { + score += 5; + IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); + if ( inSearchInfo->element.cookie == cookie ) { + score += 4; + } // cookies match + + } else { + score = 0; + } // usages match + + } else { + score = 0; + } // usage pages match + + } // if ( search usage page & usage ) + +#if LOG_SCORING + if ( kHIDPage_KeyboardOrKeypad != tElementRef->usagePage ) { // skip keyboards here + printf("%s: ( %ld:%ld )-I-Debug, score: %ld\t", + __PRETTY_FUNCTION__, + inSearchInfo->element.usagePage, + inSearchInfo->element.usage, + score); + HIDPrintElement(tIOHIDElementRef); + } + +#endif // LOG_SCORING + if ( score > bestScore ) { + bestIOHIDDeviceRef = tIOHIDDeviceRef; + bestIOHIDElementRef = tIOHIDElementRef; + bestScore = score; +#if LOG_SCORING + printf("%s: ( %ld:%ld )-I-Debug, better score: %ld\t", + __PRETTY_FUNCTION__, + inSearchInfo->element.usagePage, + inSearchInfo->element.usage, + score); + HIDPrintElement(bestIOHIDElementRef); +#endif // LOG_SCORING + } + } // for elements... + + CFRelease(gElementCFArrayRef); + gElementCFArrayRef = NULL; + } // if ( gElementCFArrayRef ) + + } // for ( devIndex = 0; devIndex < devCount; devIndex++ ) + if ( bestIOHIDDeviceRef || bestIOHIDElementRef ) { + *outFoundDevice = bestIOHIDDeviceRef; + *outFoundElement = bestIOHIDElementRef; +#if LOG_SCORING + printf("%s: ( %ld:%ld )-I-Debug, best score: %ld\t", + __PRETTY_FUNCTION__, + inSearchInfo->element.usagePage, + inSearchInfo->element.usage, + bestScore); + HIDPrintElement(bestIOHIDElementRef); +#endif // LOG_SCORING + result = TRUE; + } + + return (result); +} // HIDFindDeviceAndElement + +// --------------------------------- + +// takes input records, save required info +// assume file is open and at correct position. +// will always write to file (if file exists) size of HID_info_rec, even if device and or element is bad + +void HIDSaveElementConfig(FILE *fileRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef, int actionCookie) { + // must save: + // actionCookie + // Device: serial,vendorID, productID, location, usagePage, usage + // Element: cookie, usagePage, usage, + HID_info_rec hidInfoRec; + HIDSetElementConfig(&hidInfoRec, inIOHIDDeviceRef, inIOHIDElementRef, actionCookie); + // write to file + if ( fileRef ) { + fwrite( (void *)&hidInfoRec, sizeof(HID_info_rec), 1, fileRef ); + } +} // HIDSaveElementConfig + +// --------------------------------- + +// take file, read one record (assume file position is correct and file is open) +// search for matching device +// return pDevice, pElement and cookie for action + +int HIDRestoreElementConfig(FILE *fileRef, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef) { + // Device: serial,vendorID, productID, location, usagePage, usage + // Element: cookie, usagePage, usage, + + HID_info_rec hidInfoRec; + fread( (void *) &hidInfoRec, 1, sizeof(HID_info_rec), fileRef ); + return ( HIDGetElementConfig(&hidInfoRec, outIOHIDDeviceRef, outIOHIDElementRef) ); +} // HIDRestoreElementConfig + +// --------------------------------- + +// Set up a config record for saving +// takes an input records, returns record user can save as they want +// Note: the save rec must be pre-allocated by the calling app and will be filled out +void HIDSetElementConfig(HID_info_ptr inHIDInfoPtr, + IOHIDDeviceRef inIOHIDDeviceRef, + IOHIDElementRef inIOHIDElementRef, + int actionCookie) { + // must save: + // actionCookie + // Device: serial,vendorID, productID, location, usagePage, usage + // Element: cookie, usagePage, usage, + inHIDInfoPtr->actionCookie = actionCookie; + // device + // need to add serial number when I have a test case + if ( inIOHIDDeviceRef && inIOHIDElementRef ) { + inHIDInfoPtr->device.vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); + inHIDInfoPtr->device.productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); + inHIDInfoPtr->device.locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); + inHIDInfoPtr->device.usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); + inHIDInfoPtr->device.usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); + + inHIDInfoPtr->element.usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); + inHIDInfoPtr->element.usage = IOHIDElementGetUsage(inIOHIDElementRef); + inHIDInfoPtr->element.minReport = IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef); + inHIDInfoPtr->element.maxReport = IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef); + inHIDInfoPtr->element.cookie = IOHIDElementGetCookie(inIOHIDElementRef); + } else { + inHIDInfoPtr->device.vendorID = 0; + inHIDInfoPtr->device.productID = 0; + inHIDInfoPtr->device.locID = 0; + inHIDInfoPtr->device.usage = 0; + inHIDInfoPtr->device.usagePage = 0; + + inHIDInfoPtr->element.usagePage = 0; + inHIDInfoPtr->element.usage = 0; + inHIDInfoPtr->element.minReport = 0; + inHIDInfoPtr->element.maxReport = 0; + inHIDInfoPtr->element.cookie = 0; + } +} // HIDSetElementConfig + +// --------------------------------- +#if 0 // debug utility function to dump config record +void HIDDumpConfig(HID_info_ptr inHIDInfoPtr) { + printf( + "Config Record for action: %d\n\t vendor: %d product: %d location: %d\n\t usage: %d usagePage: %d\n\t element.usagePage: %d element.usage: %d\n\t minReport: %d maxReport: %d\n\t cookie: %d\n", + inHIDInfoPtr->actionCookie, + inHIDInfoPtr->device.vendorID, + inHIDInfoPtr->device.productID, + inHIDInfoPtr->locID, + inHIDInfoPtr->usage, + inHIDInfoPtr->usagePage, + inHIDInfoPtr->element.usagePage, + inHIDInfoPtr->element.usage, + inHIDInfoPtr->minReport, + inHIDInfoPtr->maxReport, + inHIDInfoPtr->cookie); +} // HIDDumpConfig +#endif // 0 +// --------------------------------- + +// Get matching element from config record +// takes a pre-allocated and filled out config record +// search for matching device +// return pDevice, pElement and cookie for action +int HIDGetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef) { + if ( !inHIDInfoPtr->device.locID && !inHIDInfoPtr->device.vendorID && !inHIDInfoPtr->device.productID && !inHIDInfoPtr->device.usage + && !inHIDInfoPtr->device.usagePage ) // + { // + // early out + *outIOHIDDeviceRef = NULL; + *outIOHIDElementRef = NULL; + return (inHIDInfoPtr->actionCookie); + } + + IOHIDDeviceRef tIOHIDDeviceRef, foundIOHIDDeviceRef = NULL; + IOHIDElementRef tIOHIDElementRef, foundIOHIDElementRef = NULL; + + CFIndex devIdx, devCnt, idx, cnt; + // compare to current device list for matches + // look for device + if ( inHIDInfoPtr->device.locID && inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID ) { // look for specific device + // type plug in to same port + devCnt = CFArrayGetCount(gDeviceCFArrayRef); + for ( devIdx = 0; devIdx < devCnt; devIdx++ ) { + tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIdx); + if ( !tIOHIDDeviceRef ) { + continue; // skip this device + } + + long locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); + long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); + long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); + if ( (inHIDInfoPtr->device.locID == locID) + && (inHIDInfoPtr->device.vendorID == vendorID) + && (inHIDInfoPtr->device.productID == productID) ) + { + foundIOHIDDeviceRef = tIOHIDDeviceRef; + } + if ( foundIOHIDDeviceRef ) { + break; + } + } // next devIdx + if ( foundIOHIDDeviceRef ) { + CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); + if ( elementCFArrayRef ) { + cnt = CFArrayGetCount(elementCFArrayRef); + for ( idx = 0; idx < cnt; idx++ ) { + tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); + if ( !tIOHIDElementRef ) { + continue; // skip this element + } + + IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); + if ( inHIDInfoPtr->element.cookie == cookie ) { + foundIOHIDElementRef = tIOHIDElementRef; + } + if ( foundIOHIDElementRef ) { + break; + } + } + if ( !foundIOHIDElementRef ) { + cnt = CFArrayGetCount(elementCFArrayRef); + for ( idx = 0; idx < cnt; idx++ ) { + tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); + if ( !tIOHIDElementRef ) { + continue; // skip this element + } + + uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); + uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); + if ( (inHIDInfoPtr->element.usage == usage) && (inHIDInfoPtr->element.usagePage == usagePage) ) { + foundIOHIDElementRef = tIOHIDElementRef; + } + if ( foundIOHIDElementRef ) { + break; + } + } // next idx + + } // if ( !foundIOHIDElementRef ) + if ( foundIOHIDElementRef ) { // if same device + // setup the calibration + IOHIDElement_SetupCalibration(tIOHIDElementRef); + + IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, inHIDInfoPtr->element.minReport); + IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, inHIDInfoPtr->element.maxReport); + } + + CFRelease(elementCFArrayRef); + } // if ( elementCFArrayRef ) + + } // if ( foundIOHIDDeviceRef ) + // if we have not found a match, look at just vendor and product + if ( (!foundIOHIDDeviceRef) && (inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID) ) { + devCnt = CFArrayGetCount(gDeviceCFArrayRef); + for ( devIdx = 0; devIdx < devCnt; devIdx++ ) { + tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIdx); + if ( !tIOHIDDeviceRef ) { + continue; // skip this device + } + + long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); + long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); + if ( (inHIDInfoPtr->device.vendorID == vendorID) + && (inHIDInfoPtr->device.productID == productID) ) + { + foundIOHIDDeviceRef = tIOHIDDeviceRef; + } + if ( foundIOHIDDeviceRef ) { + break; + } + } + // match elements by cookie since same device type + if ( foundIOHIDDeviceRef ) { + CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); + if ( elementCFArrayRef ) { + cnt = CFArrayGetCount(elementCFArrayRef); + for ( idx = 0; idx < cnt; idx++ ) { + tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); + if ( !tIOHIDElementRef ) { + continue; // skip this element + } + + IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); + if ( inHIDInfoPtr->element.cookie == cookie ) { + foundIOHIDElementRef = tIOHIDElementRef; + } + if ( foundIOHIDElementRef ) { + break; + } + } + // if no cookie match (should NOT occur) match on usage + if ( !foundIOHIDElementRef ) { + cnt = CFArrayGetCount(elementCFArrayRef); + for ( idx = 0; idx < cnt; idx++ ) { + tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); + if ( !tIOHIDElementRef ) { + continue; // skip this element + } + + uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); + uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); + if ( (inHIDInfoPtr->element.usage == usage) + && (inHIDInfoPtr->element.usagePage == usagePage) ) + { + foundIOHIDElementRef = tIOHIDElementRef; + } + if ( foundIOHIDElementRef ) { + break; + } + } // next idx + + } // if ( !foundIOHIDElementRef ) + if ( foundIOHIDElementRef ) { // if same device + // setup the calibration + IOHIDElement_SetupCalibration(tIOHIDElementRef); + IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, inHIDInfoPtr->element.minReport); + IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, inHIDInfoPtr->element.maxReport); + } + + CFRelease(elementCFArrayRef); + } // if ( elementCFArrayRef ) + + } // if ( foundIOHIDDeviceRef ) + + } // if ( device not found & vendorID & productID ) + + } // if ( inHIDInfoPtr->locID && inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID ) + // can't find matching device return NULL, do not return first device + if ( (!foundIOHIDDeviceRef) || (!foundIOHIDElementRef) ) { + // no HID device + *outIOHIDDeviceRef = NULL; + *outIOHIDElementRef = NULL; + return (inHIDInfoPtr->actionCookie); + } else { + // HID device + *outIOHIDDeviceRef = foundIOHIDDeviceRef; + *outIOHIDElementRef = foundIOHIDElementRef; + return (inHIDInfoPtr->actionCookie); + } +} // HIDGetElementConfig + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/HID_Error_Handler.c b/src/cocoa/HID_Error_Handler.c new file mode 100644 index 000000000..0802a2ca7 --- /dev/null +++ b/src/cocoa/HID_Error_Handler.c @@ -0,0 +1,108 @@ +// File: HID_Error_Handler.c +// Abstract: Implementation of the HID utilities error handlers +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + +#ifdef DEBUG // not used in release +#if !defined (kBuildingLibrary) +#define kVerboseErrors + +// system includes ---------------------------------------------------------- + +#ifdef kVerboseErrors +//#include +#endif +#endif // not kBuildingLibrary +#endif // DEBUG + +#include + +// project includes --------------------------------------------------------- + +#include "HID_Utilities_External.h" + +// globals (internal/private) ----------------------------------------------- + +// prototypes (internal/private) -------------------------------------------- + +// functions (internal/private) --------------------------------------------- + +#pragma mark - +// ------------------------------------- + +// central error reporting + +void HIDReportErrorNum(const char *strError, int numError) { + char errMsgCStr[256]; + + sprintf(errMsgCStr, "%s #%d (0x%x)", strError, numError, numError); + + // out as debug string +#ifdef kVerboseErrors + { + fputs(errMsgCStr, stderr); + } +#endif // kVerboseErrors +} // HIDReportErrorNum + +// ------------------------------------- + +void HIDReportError(const char *strError) { + char errMsgCStr[256]; + + sprintf(errMsgCStr, "%s", strError); + + // out as debug string +#ifdef kVerboseErrors + { + fputs(errMsgCStr, stderr); + } +#endif // kVerboseErrors +} // HIDReportError + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/HID_Name_Lookup.c b/src/cocoa/HID_Name_Lookup.c new file mode 100644 index 000000000..c9a1ca264 --- /dev/null +++ b/src/cocoa/HID_Name_Lookup.c @@ -0,0 +1,1210 @@ +// File: HID_Name_Lookup.c +// Abstract: HID Name Lookup Utilities. +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + +#pragma mark - includes & imports +//***************************************************** +#include "HID_Utilities_External.h" +//***************************************************** +#pragma mark - typedefs, enums, defines, etc. +//***************************************************** +#define FAKE_MISSING_NAMES 0 // for debugging; returns the vendor, product & cookie ( or usage info ) as numbers. +#define VERBOSE_ELEMENT_NAMES 0 // set TRUE to include vender & product names in element names ( useful for debugging ) + +#define kNameKeyCFStringRef CFSTR("Name") +//***************************************************** +#pragma mark - local ( static ) function prototypes +//***************************************************** + +#if 0 // currently unused +static SInt32 hu_SaveToXMLFile(CFPropertyListRef inCFPRef, CFURLRef inCFURLRef); +static SInt32 hu_XMLSave(CFPropertyListRef inCFPropertyListRef, CFStringRef inResourceName, CFStringRef inResourceExtension); +#endif +static CFPropertyListRef hu_LoadFromXMLFile(CFURLRef inCFURLRef); +static CFPropertyListRef hu_XMLLoad(CFStringRef inResourceName, CFStringRef inResourceExtension); + +static Boolean hu_XMLSearchForElementNameByCookie(long inVendorID, long inProductID, IOHIDElementCookie inCookie, char *outCStr); +static Boolean hu_XMLSearchForElementNameByUsage(long inVendorID, long inProductID, long inUsagePage, long inUsage, char *outCStr); + +static Boolean hu_XMLSearchForVendorNameByVendorID(long inVendorID, char *outCStr); +static Boolean hu_XMLSearchForProductNameByVendorProductID(long inVendorID, long inProductID, char *outCStr); + +#if 0 // currently unused +static Boolean hu_AddVendorProductToCFDict(CFMutableDictionaryRef inCFMutableDictionaryRef, + long inVendorID, + CFStringRef inVendorCFStringRef, + long inProductID, + CFStringRef inProductCFStringRef); +static Boolean hu_AddDeviceElementToUsageXML(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef); +#endif +//***************************************************** +#pragma mark - exported globals +//***************************************************** +#pragma mark - local ( static ) globals +//***************************************************** +static CFPropertyListRef gCookieCFPropertyListRef = NULL; +static CFPropertyListRef gUsageCFPropertyListRef = NULL; + +//***************************************************** +#pragma mark - exported function implementations +//***************************************************** + +/************************************************************************* + * + * HIDGetVendorNameFromVendorID( inVendorID, inProductID, inCookie, outCStrName ) + * + * Purpose: Uses an devices vendor ID to generate a name for it. + * + * Notes: Now uses XML files to store dictionary of names + * + * Inputs: inVendorID - the elements vendor ID + * outCStrName - address where result will be returned + * Returns: Boolean - if successful + */ +Boolean HIDGetVendorNameFromVendorID(long inVendorID, char *outCStrName) { + Boolean result = FALSE; + *outCStrName = 0; // clear name + if ( hu_XMLSearchForVendorNameByVendorID(inVendorID, outCStrName) ) { + return (TRUE); + } + +#if FAKE_MISSING_NAMES + sprintf(outCStrName, "#{ V: %ld}#", inVendorID); + result = TRUE; +#endif // FAKE_MISSING_NAMES + return (result); +} // HIDGetVendorNameFromVendorID + +/************************************************************************* + * + * HIDGetProductNameFromVendorProductID( inVendorID, inProductID, outCStrName ) + * + * Purpose: Uses an elements vendor, product & usage info to generate a name for it. + * + * Notes: Now uses XML files to store dictionary of names + * + * Inputs: inVendorID - the elements vendor ID + * inProductID - the elements product ID + * inUsagePage - the elements usage page + * inUsage - the elements usage + * outCStrName - address where result will be returned + * Returns: Boolean - if successful + */ +Boolean HIDGetProductNameFromVendorProductID(long inVendorID, long inProductID, char *outCStrName) { + Boolean result = FALSE; + *outCStrName = 0; // clear name + if ( hu_XMLSearchForProductNameByVendorProductID(inVendorID, inProductID, outCStrName) ) { + return (TRUE); + } + +#if FAKE_MISSING_NAMES + sprintf(outCStrName, "#{ V: %ld, P: %ld, U: %ld: %ld}#", inVendorID, inProductID, inUsagePage, inUsage); + result = TRUE; +#endif // FAKE_MISSING_NAMES + return (result); +} // HIDGetProductNameFromVendorProductID + +/************************************************************************* + * + * HIDGetElementNameFromVendorProductCookie( inVendorID, inProductID, inCookie, outCStrName ) + * + * Purpose: Uses an elements vendor, product & cookie to generate a name for it. + * + * Notes: Now uses XML files to store dictionary of names + * + * Inputs: inVendorID - the elements vendor ID + * inProductID - the elements product ID + * inCookie - the elements cookie + * outCStrName - address where result will be returned + * Returns: Boolean - if successful + */ +Boolean HIDGetElementNameFromVendorProductCookie(int inVendorID, int inProductID, IOHIDElementCookie inCookie, char *outCStrName) { + Boolean result = FALSE; + *outCStrName = 0; // clear name + // Look in the XML file first + if ( hu_XMLSearchForElementNameByCookie(inVendorID, inProductID, inCookie, outCStrName) ) { + return (TRUE); + } + +#if FAKE_MISSING_NAMES + sprintf(outCStrName, "#{ V: %ld, P: %ld, C: %ld}#", inVendorID, inProductID, inCookie); +#else + result = FALSE; +#endif // FAKE_MISSING_NAMES + return (result); +} // HIDGetElementNameFromVendorProductCookie + +/************************************************************************* + * + * HIDGetElementNameFromVendorProductUsage( inVendorID, inProductID, inUsagePage, inUsage, outCStrName ) + * + * Purpose: Uses an elements vendor, product & usage info to generate a name for it. + * + * Notes: Now uses XML files to store dictionary of names + * + * Inputs: inVendorID - the elements vendor ID + * inProductID - the elements product ID + * inUsagePage - the elements usage page + * inUsage - the elements usage + * outCStrName - address where result will be returned + * Returns: Boolean - if successful + */ +Boolean HIDGetElementNameFromVendorProductUsage(long inVendorID, + long inProductID, + long inUsagePage, + long inUsage, + char *outCStrName) { + Boolean result = FALSE; + *outCStrName = 0; // clear name + if ( hu_XMLSearchForElementNameByUsage(inVendorID, inProductID, inUsagePage, inUsage, outCStrName) ) { + return (TRUE); + } + +#if FAKE_MISSING_NAMES + sprintf(outCStrName, "#{ V: %ld, P: %ld, U: %ld: %ld}#", inVendorID, inProductID, inUsagePage, inUsage); + result = TRUE; +#endif // FAKE_MISSING_NAMES + return (result); +} // HIDGetElementNameFromVendorProductUsage + +#if 0 // currently unused +/************************************************************************* + * + * HIDAddDeviceToXML( inDevice ) + * + * Purpose: Adds a devices info to the HID_device_usage_strings.plist( XML ) file + * + * Inputs: inDevice - the device + * Returns: Boolean - if successful + */ +static Boolean HIDAddDeviceToXML(IOHIDDeviceRef inIOHIDDeviceRef) { + Boolean result = FALSE; + if ( HIDIsValidDevice(inIOHIDDeviceRef) ) { + CFStringRef vendorCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); + CFStringRef productCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); + if ( vendorCFStringRef && productCFStringRef ) { +#if 0 // don't update the cookie xml file + gCookieCFPropertyListRef = + hu_XMLLoad( CFSTR( + "HID_cookie_strings"), CFSTR("plist") ); + if ( gCookieCFPropertyListRef ) { + CFMutableDictionaryRef tCFMutableDictionaryRef = + CFDictionaryCreateMutableCopy( + kCFAllocatorDefault, + 0, + gCookieCFPropertyListRef); + if ( tCFMutableDictionaryRef ) { + if ( hu_AddVendorProductToCFDict(tCFMutableDictionaryRef, vendorID, vendorCFStringRef, productID, + productCFStringRef) ) + { + hu_XMLSave( tCFMutableDictionaryRef, + CFSTR( + "HID_cookie_strings"), CFSTR("plist") ); + result = TRUE; + } + + CFRelease(tCFMutableDictionaryRef); + } + } + +#endif + if ( gUsageCFPropertyListRef ) { + CFRelease(gUsageCFPropertyListRef); + } + + gUsageCFPropertyListRef = + hu_XMLLoad( CFSTR( + "HID_device_usage_strings"), CFSTR("plist") ); + if ( gUsageCFPropertyListRef ) { + CFMutableDictionaryRef tCFMutableDictionaryRef = + CFDictionaryCreateMutableCopy( + kCFAllocatorDefault, + 0, + gUsageCFPropertyListRef); + if ( tCFMutableDictionaryRef ) { + long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); + long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); + if ( hu_AddVendorProductToCFDict(tCFMutableDictionaryRef, vendorID, vendorCFStringRef, productID, + productCFStringRef) ) + { + hu_XMLSave( tCFMutableDictionaryRef, + CFSTR( + "HID_device_usage_strings"), CFSTR("plist") ); + result = TRUE; + } + + CFRelease(tCFMutableDictionaryRef); + } + } + } + } + + return (result); +} // HIDAddDeviceToXML + +/************************************************************************* + * + * HIDAddDeviceElementToXML( inDevice, inElement ) + * + * Purpose: Adds a devices info to the HID_device_usage_strings.plist( XML ) file + * + * Inputs: inDevice - the device + * inElement - the element + * + * Returns: Boolean - if successful + */ +Boolean HIDAddDeviceElementToXML(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { + Boolean result = FALSE; + if ( HIDIsValidElement(inIOHIDElementRef) ) { + if ( HIDAddDeviceToXML(inIOHIDDeviceRef) ) { + result = TRUE; + } + if ( hu_AddDeviceElementToUsageXML(inIOHIDDeviceRef, inIOHIDElementRef) ) { + result = TRUE; + } + } + + return (result); +} // HIDAddDeviceElementToXML +#endif +/************************************************************************* + * + * HIDGetTypeName( inIOHIDElementType, outCStrName ) + * + * Purpose: return a C string for a given element type( see IOHIDKeys.h ) + * Notes: returns "Unknown Type" for invalid types + * + * Inputs: inIOHIDElementType - type element type + * outCStrName - address where to store element type string + * + * Returns: outCStrName - the element type string + */ + +void HIDGetTypeName(IOHIDElementType inIOHIDElementType, char *outCStrName) { + switch ( inIOHIDElementType ) { + case kIOHIDElementTypeInput_Misc: + { + sprintf(outCStrName, "Miscellaneous Input"); + break; + } + + case kIOHIDElementTypeInput_Button: + { + sprintf(outCStrName, "Button Input"); + break; + } + + case kIOHIDElementTypeInput_Axis: + { + sprintf(outCStrName, "Axis Input"); + break; + } + + case kIOHIDElementTypeInput_ScanCodes: + { + sprintf(outCStrName, "Scan Code Input"); + break; + } + + case kIOHIDElementTypeOutput: + { + sprintf(outCStrName, "Output"); + break; + } + + case kIOHIDElementTypeFeature: + { + sprintf(outCStrName, "Feature"); + break; + } + + case kIOHIDElementTypeCollection: + { + sprintf(outCStrName, "Collection"); + break; + } + + default: + { + sprintf(outCStrName, "Unknown Type"); + break; + } + } // switch + +} // HIDGetTypeName + +//************************************************************************* +// +// HIDCopyUsageName( inUsagePage, inUsage ) +// +// Purpose: return a CFStringRef string for a given usage page & usage( see IOUSBHIDParser.h ) +// +// Notes: returns usage page and usage values in CFString form for unknown values +// +// Inputs: inUsagePage - the usage page +// inUsage - the usage +// +// Returns: CFStringRef - the resultant string +// + +CFStringRef HIDCopyUsageName(long inUsagePage, long inUsage) { + static CFPropertyListRef tCFPropertyListRef = NULL; + CFStringRef result = NULL; + if ( !tCFPropertyListRef ) { + tCFPropertyListRef = + hu_XMLLoad( CFSTR( + "HID_usage_strings"), CFSTR("plist") ); + } + if ( tCFPropertyListRef ) { + if ( + CFDictionaryGetTypeID() == CFGetTypeID(tCFPropertyListRef) ) + { + CFStringRef pageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("0x%4.4lX"), inUsagePage); + if ( pageKeyCFStringRef ) { + CFDictionaryRef pageCFDictionaryRef; + if ( CFDictionaryGetValueIfPresent(tCFPropertyListRef, pageKeyCFStringRef, + (const void **) &pageCFDictionaryRef) ) + { + CFStringRef pageCFStringRef; + if ( CFDictionaryGetValueIfPresent(pageCFDictionaryRef, kNameKeyCFStringRef, + (const void **) &pageCFStringRef) ) + { + CFStringRef usageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "0x%4.4lX"), inUsage); + if ( usageKeyCFStringRef ) { + CFStringRef usageCFStringRef; + if ( CFDictionaryGetValueIfPresent(pageCFDictionaryRef, usageKeyCFStringRef, + (const void **) &usageCFStringRef) ) + { + result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "%@ %@"), pageCFStringRef, usageCFStringRef); + } + +#if FAKE_MISSING_NAMES + else { + result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "%@ #%@"), pageCFStringRef, usageKeyCFStringRef); + } +#endif + CFRelease(usageKeyCFStringRef); + } + } else { + // no name data for this page + } + } else { + // no data for this page + } + + CFRelease(pageKeyCFStringRef); + } + } + + // CFRelease( tCFPropertyListRef ); // Leak this! + // tCFPropertyListRef = NULL; + } + + return (result); +} // HIDCopyUsageName + +//***************************************************** +#pragma mark - local ( static ) function implementations +//***************************************************** +#if 0 // currently unused +/************************************************************************* + * + * hu_SaveToXMLFile( inCFPRef, inCFURLRef ) + * + * Purpose: save a property list into an XML file + * + * Inputs: inCFPRef - the data + * inCFURLRef - URL for the file + * + * Returns: SInt32 - error code ( if any ) + */ +static SInt32 hu_SaveToXMLFile(CFPropertyListRef inCFPRef, CFURLRef inCFURLRef) { + CFDataRef xmlCFDataRef; + SInt32 error = coreFoundationUnknownErr; + + // Convert the property list into XML data. + xmlCFDataRef = CFPropertyListCreateXMLData(kCFAllocatorDefault, inCFPRef); + if ( xmlCFDataRef ) { + // Write the XML data to the file. + (void) CFURLWriteDataAndPropertiesToResource(inCFURLRef, xmlCFDataRef, NULL, &error); + + // Release the XML data + CFRelease(xmlCFDataRef); + } + + return (error); +} // hu_SaveToXMLFile +#endif +/************************************************************************* + * + * hu_LoadFromXMLFile( inCFURLRef ) + * + * Purpose: load a property list from an XML file + * + * Inputs: inCFURLRef - URL for the file + * + * Returns: CFPropertyListRef - the data + */ +static CFPropertyListRef hu_LoadFromXMLFile(CFURLRef inCFURLRef) { + CFDataRef xmlCFDataRef; + CFPropertyListRef myCFPropertyListRef = NULL; + + // Read the XML file. + SInt32 error; + if ( CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, inCFURLRef, &xmlCFDataRef, NULL, NULL, &error) ) { + CFStringRef errorString; + // Reconstitute the dictionary using the XML data. + myCFPropertyListRef = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, + xmlCFDataRef, + kCFPropertyListImmutable, + &errorString); + // Release the XML data + CFRelease(xmlCFDataRef); + } + + return (myCFPropertyListRef); +} // hu_LoadFromXMLFile + +#if 0 // currently unused +/************************************************************************* + * + * hu_XMLSave( inCFPropertyListRef, inResourceName, inResourceExtension ) + * + * Purpose: save a CFPropertyListRef into a resource( XML ) file + * + * Inputs: inCFPropertyListRef - the data + * inResourceName - name of the resource file + * inResourceExtension - extension of the resource file + * + * Returns: SInt32 - error code ( if any ) + */ +static SInt32 hu_XMLSave(CFPropertyListRef inCFPropertyListRef, CFStringRef inResourceName, CFStringRef inResourceExtension) { + CFURLRef resFileCFURLRef; + SInt32 error = -1; + + // check the main (application) bundle + resFileCFURLRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(), inResourceName, inResourceExtension, NULL); + + if ( !resFileCFURLRef ) { + // check this specific (HID_Utilities framework) bundle + CFBundleRef tCFBundleRef = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HID_Utilities")); + if ( tCFBundleRef ) { + resFileCFURLRef = CFBundleCopyResourceURL( tCFBundleRef, inResourceName, inResourceExtension, NULL ); + } + } + if ( !resFileCFURLRef ) { + // check bundles already loaded or otherwise known to the current process + CFArrayRef tCFArrayRef = CFBundleGetAllBundles(); + CFIndex idx, cnt = CFArrayGetCount(tCFArrayRef); + for ( idx = 0; idx < cnt; idx++ ) { + CFBundleRef tCFBundleRef = (CFBundleRef) CFArrayGetValueAtIndex(tCFArrayRef, idx) ; + if ( tCFBundleRef ) { + resFileCFURLRef = CFBundleCopyResourceURL( tCFBundleRef, inResourceName, inResourceExtension, NULL ); + if ( resFileCFURLRef ) { + break; + } + } + } + } + if ( resFileCFURLRef ) { + error = hu_SaveToXMLFile(inCFPropertyListRef, resFileCFURLRef); + CFRelease(resFileCFURLRef); + } + + return (error); +} // hu_XMLSave +#endif +/************************************************************************* + * + * hu_XMLLoad( inResourceName, inResourceExtension ) + * + * Purpose: Load a resource( XML ) file into a CFPropertyListRef + * + * Inputs: inResourceName - name of the resource file + * inResourceExtension - extension of the resource file + * + * Returns: CFPropertyListRef - the data + */ +static CFPropertyListRef hu_XMLLoad(CFStringRef inResourceName, CFStringRef inResourceExtension) { + CFURLRef resFileCFURLRef; + CFPropertyListRef tCFPropertyListRef = NULL; + + // check the main (application) bundle + resFileCFURLRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(), inResourceName, inResourceExtension, NULL); + + if ( !resFileCFURLRef ) { + // check this specific (HID_Utilities framework) bundle + CFBundleRef tCFBundleRef = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HID_Utilities")); + if ( tCFBundleRef ) { + resFileCFURLRef = CFBundleCopyResourceURL( tCFBundleRef, inResourceName, inResourceExtension, NULL ); + } + } + if ( !resFileCFURLRef ) { + // check bundles already loaded or otherwise known to the current process + CFArrayRef tCFArrayRef = CFBundleGetAllBundles(); + CFIndex idx, cnt = CFArrayGetCount(tCFArrayRef); + for ( idx = 0; idx < cnt; idx++ ) { + CFBundleRef tCFBundleRef = (CFBundleRef) CFArrayGetValueAtIndex(tCFArrayRef, idx) ; + if ( tCFBundleRef ) { + resFileCFURLRef = CFBundleCopyResourceURL( tCFBundleRef, inResourceName, inResourceExtension, NULL ); + if ( resFileCFURLRef ) { + break; + } + } + } + } + if ( resFileCFURLRef ) { + tCFPropertyListRef = hu_LoadFromXMLFile(resFileCFURLRef); + CFRelease(resFileCFURLRef); + } + + return (tCFPropertyListRef); +} // hu_XMLLoad + +/************************************************************************* + * + * hu_XMLSearchForVendorNameByVendorID( inVendorID, outCStr ) + * + * Purpose: Find a vendor string in the resource ( XML ) file + * + * Inputs: inVendorID - the elements vendor ID + * inProductID - the elements product ID + * outCStr - address where result will be returned + * + * Returns: Boolean - if successful + */ +static Boolean hu_XMLSearchForVendorNameByVendorID(long inVendorID, char *outCStr) { + Boolean results = FALSE; + if ( !gUsageCFPropertyListRef ) { + gUsageCFPropertyListRef = + hu_XMLLoad( CFSTR( + "HID_device_usage_strings"), CFSTR("plist") ); + } + if ( gUsageCFPropertyListRef ) { + if ( + CFDictionaryGetTypeID() == CFGetTypeID(gUsageCFPropertyListRef) ) + { + CFDictionaryRef vendorCFDictionaryRef; + CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inVendorID); + if ( vendorKeyCFStringRef ) { + if ( CFDictionaryGetValueIfPresent(gUsageCFPropertyListRef, vendorKeyCFStringRef, + (const void **) &vendorCFDictionaryRef) ) + { + CFStringRef vendorCFStringRef = NULL; + if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, kNameKeyCFStringRef, + (const void **) &vendorCFStringRef) && vendorCFStringRef ) + { + // CFShow( vendorCFStringRef ); + results = + CFStringGetCString(vendorCFStringRef, outCStr, CFStringGetLength( + vendorCFStringRef) * sizeof(UniChar) + 1, kCFStringEncodingUTF8); + } + } + + CFRelease(vendorKeyCFStringRef); + } + } + + // ++ CFRelease( gUsageCFPropertyListRef ); // Leak this ! + } + + return (results); +} // hu_XMLSearchForVendorNameByVendorID + +/************************************************************************* + * + * hu_XMLSearchForProductNameByVendorProductID( inVendorID, inProductID, outCStr ) + * + * Purpose: Find an product string in the resource ( XML ) file + * + * Inputs: inVendorID - the elements vendor ID + * inProductID - the elements product ID + * outCStr - address where result will be returned + * + * Returns: Boolean - if successful + */ +static Boolean hu_XMLSearchForProductNameByVendorProductID(long inVendorID, long inProductID, char *outCStr) { + Boolean results = FALSE; + if ( !gUsageCFPropertyListRef ) { + gUsageCFPropertyListRef = + hu_XMLLoad( CFSTR( + "HID_device_usage_strings"), CFSTR("plist") ); + } + if ( gUsageCFPropertyListRef ) { + if ( + CFDictionaryGetTypeID() == CFGetTypeID(gUsageCFPropertyListRef) ) + { + // first we make our vendor ID key + CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inVendorID); + if ( vendorKeyCFStringRef ) { + // and use it to look up our vendor dictionary + CFDictionaryRef vendorCFDictionaryRef; + if ( CFDictionaryGetValueIfPresent(gUsageCFPropertyListRef, vendorKeyCFStringRef, + (const void **) &vendorCFDictionaryRef) ) + { + // pull our vendor name our of that dictionary + CFStringRef vendorCFStringRef = NULL; + if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, kNameKeyCFStringRef, + (const void **) &vendorCFStringRef) ) + { +#if FAKE_MISSING_NAMES + CFRetain(vendorCFStringRef); // so we can CFRelease it later + } else { + vendorCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "V: %@"), vendorKeyCFStringRef); +#endif + } + + // now we make our product ID key + CFStringRef productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "%ld"), inProductID); + if ( productKeyCFStringRef ) { + // and use that key to look up our product dictionary in the vendor dictionary + CFDictionaryRef productCFDictionaryRef; + if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, productKeyCFStringRef, + (const void **) &productCFDictionaryRef) ) + { + // pull our product name our of the product dictionary + CFStringRef productCFStringRef = NULL; + if ( CFDictionaryGetValueIfPresent(productCFDictionaryRef, kNameKeyCFStringRef, + (const void **) &productCFStringRef) ) + { +#if FAKE_MISSING_NAMES + CFRetain(productCFStringRef); // so we can CFRelease it later + } else { + productCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "P: %@"), kNameKeyCFStringRef); +#endif + } + + CFStringRef fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "%@ %@"), vendorCFStringRef, + productCFStringRef); + if ( fullCFStringRef ) { + // CFShow( fullCFStringRef ); + results = + CFStringGetCString(fullCFStringRef, outCStr, CFStringGetLength( + fullCFStringRef) * sizeof(UniChar) + 1, kCFStringEncodingUTF8); + CFRelease(fullCFStringRef); + } + +#if FAKE_MISSING_NAMES + if ( productCFStringRef ) { + CFRelease(productCFStringRef); + } + +#endif + } + + CFRelease(productKeyCFStringRef); + } + +#if FAKE_MISSING_NAMES + if ( vendorCFStringRef ) { + CFRelease(vendorCFStringRef); + } + +#endif + } + + CFRelease(vendorKeyCFStringRef); + } + } + + // ++ CFRelease( gUsageCFPropertyListRef ); // Leak this ! + } + + return (results); +} // hu_XMLSearchForProductNameByVendorProductID + +/************************************************************************* + * + * hu_XMLSearchForElementNameByCookie( inVendorID, inProductID, inCookie, outCStr ) + * + * Purpose: Find an element string in the resource( XML ) file + * + * Inputs: inVendorID - the elements vendor ID + * inProductID - the elements product ID + * inCookie - the elements cookie + * outCStr - address where result will be returned + * + * Returns: Boolean - if successful + */ +static Boolean hu_XMLSearchForElementNameByCookie(long inVendorID, long inProductID, IOHIDElementCookie inCookie, char *outCStr) { + Boolean results = FALSE; + if ( !gCookieCFPropertyListRef ) { + gCookieCFPropertyListRef = + hu_XMLLoad( CFSTR( + "HID_cookie_strings"), CFSTR("plist") ); + } + if ( gCookieCFPropertyListRef ) { + if ( + CFDictionaryGetTypeID() == CFGetTypeID(gCookieCFPropertyListRef) ) + { + CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inVendorID); + if ( vendorKeyCFStringRef ) { + CFDictionaryRef vendorCFDictionaryRef; + if ( CFDictionaryGetValueIfPresent(gCookieCFPropertyListRef, vendorKeyCFStringRef, + (const void **) &vendorCFDictionaryRef) ) + { + CFDictionaryRef productCFDictionaryRef; + CFStringRef productKeyCFStringRef; + CFStringRef vendorCFStringRef; + if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, kNameKeyCFStringRef, + (const void **) &vendorCFStringRef) ) + { + // CFShow( vendorCFStringRef ); + } + + productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inProductID); + if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, productKeyCFStringRef, + (const void **) &productCFDictionaryRef) ) + { + CFStringRef fullCFStringRef = NULL; + CFStringRef cookieKeyCFStringRef; + CFStringRef productCFStringRef; + CFStringRef cookieCFStringRef; + if ( CFDictionaryGetValueIfPresent(productCFDictionaryRef, kNameKeyCFStringRef, + (const void **) &productCFStringRef) ) + { + // CFShow( productCFStringRef ); + } + + cookieKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inCookie); + if ( CFDictionaryGetValueIfPresent(productCFDictionaryRef, cookieKeyCFStringRef, + (const void **) &cookieCFStringRef) ) + { +#if VERBOSE_ELEMENT_NAMES + fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "%@ %@ %@"), vendorCFStringRef, productCFStringRef, + cookieCFStringRef); +#else + fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), cookieCFStringRef); +#endif // VERBOSE_ELEMENT_NAMES + // CFShow( cookieCFStringRef ); + } + +#if FAKE_MISSING_NAMES + else { + fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "%@ %@ # %@"), vendorCFStringRef, productCFStringRef, + cookieKeyCFStringRef); + } +#endif // FAKE_MISSING_NAMES + if ( fullCFStringRef ) { + // CFShow( fullCFStringRef ); + results = + CFStringGetCString(fullCFStringRef, outCStr, CFStringGetLength( + fullCFStringRef) * sizeof(UniChar) + 1, kCFStringEncodingUTF8); + CFRelease(fullCFStringRef); + } + + CFRelease(cookieKeyCFStringRef); + } + + CFRelease(productKeyCFStringRef); + } + + CFRelease(vendorKeyCFStringRef); + } + } + + // ++ CFRelease( gCookieCFPropertyListRef ); // Leak this ! + } + + return (results); +} // hu_XMLSearchForElementNameByCookie + +/************************************************************************* + * + * hu_XMLSearchForElementNameByUsage( inVendorID, inProductID, inUsagePage, inUsage, outCStr ) + * + * Purpose: Find an element string in the resource( XML ) file + * + * Inputs: inVendorID - the elements vendor ID + * inProductID - the elements product ID + * inUsagePage - the elements usage page + * inUsage - the elements usage + * outCStr - address where result will be returned + * + * Returns: Boolean - if successful + */ +static Boolean hu_XMLSearchForElementNameByUsage(long inVendorID, long inProductID, long inUsagePage, long inUsage, + char *outCStr) { + Boolean results = FALSE; + if ( !gUsageCFPropertyListRef ) { + gUsageCFPropertyListRef = + hu_XMLLoad( CFSTR( + "HID_device_usage_strings"), CFSTR("plist") ); + } + if ( gUsageCFPropertyListRef ) { + if ( + CFDictionaryGetTypeID() == CFGetTypeID(gUsageCFPropertyListRef) ) + { + CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inVendorID); + if ( vendorKeyCFStringRef ) { + CFDictionaryRef vendorCFDictionaryRef; + if ( CFDictionaryGetValueIfPresent(gUsageCFPropertyListRef, vendorKeyCFStringRef, + (const void **) &vendorCFDictionaryRef) ) + { + CFStringRef vendorCFStringRef = NULL; + if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, kNameKeyCFStringRef, + (const void **) &vendorCFStringRef) ) + { + vendorCFStringRef = CFStringCreateCopy(kCFAllocatorDefault, vendorCFStringRef); + } else { + vendorCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("v: %ld"), inVendorID); + // CFShow( vendorCFStringRef ); + } + + CFStringRef productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "%ld"), inProductID); + + CFDictionaryRef productCFDictionaryRef; + if ( CFDictionaryGetValueIfPresent(vendorCFDictionaryRef, productKeyCFStringRef, + (const void **) &productCFDictionaryRef) ) + { + CFStringRef fullCFStringRef = NULL; + + CFStringRef productCFStringRef; + if ( CFDictionaryGetValueIfPresent(productCFDictionaryRef, kNameKeyCFStringRef, + (const void **) &productCFStringRef) ) + { + // CFShow( productCFStringRef ); + } + + CFStringRef usageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "%ld:%ld"), inUsagePage, inUsage); + CFStringRef usageCFStringRef; + if ( CFDictionaryGetValueIfPresent(productCFDictionaryRef, usageKeyCFStringRef, + (const void **) &usageCFStringRef) ) + { +#if VERBOSE_ELEMENT_NAMES + fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "%@ %@ %@"), vendorCFStringRef, productCFStringRef, + usageCFStringRef); +#else + fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), usageCFStringRef); +#endif // VERBOSE_ELEMENT_NAMES + // CFShow( usageCFStringRef ); + } + +#if FAKE_MISSING_NAMES + else { + fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( + "%@ %@ # %@"), vendorCFStringRef, productCFStringRef, + usageKeyCFStringRef); + } +#endif // FAKE_MISSING_NAMES + if ( fullCFStringRef ) { + // CFShow( fullCFStringRef ); + results = + CFStringGetCString(fullCFStringRef, outCStr, CFStringGetLength( + fullCFStringRef) * sizeof(UniChar) + 1, kCFStringEncodingUTF8); + CFRelease(fullCFStringRef); + } + + CFRelease(usageKeyCFStringRef); + } + if ( vendorCFStringRef ) { + CFRelease(vendorCFStringRef); + } + + CFRelease(productKeyCFStringRef); + } + + CFRelease(vendorKeyCFStringRef); + } + } + + // ++ CFRelease( gUsageCFPropertyListRef ); // Leak this ! + } + + return (results); +} // hu_XMLSearchForElementNameByUsage + +#if 0 // currently unused +/************************************************************************* + * + * hu_AddVendorProductToCFDict( inCFMutableDictionaryRef, inVendorID, inVendorCFStringRef, inProductID, inProductCFStringRef ) + * + * Purpose: add a vendor & product to a dictionary + * + * Inputs: inCFMutableDictionaryRef - the dictionary + * inVendorID - the elements vendor ID + * inProductID - the elements product ID + * inProductCFStringRef - the string to be added + * + * Returns: Boolean - if successful + */ +static Boolean hu_AddVendorProductToCFDict(CFMutableDictionaryRef inCFMutableDictionaryRef, + long inVendorID, + CFStringRef inVendorCFStringRef, + long inProductID, + CFStringRef inProductCFStringRef) { + Boolean results = FALSE; + if ( inCFMutableDictionaryRef && ( CFDictionaryGetTypeID() == CFGetTypeID(inCFMutableDictionaryRef) ) ) { + CFMutableDictionaryRef vendorCFMutableDictionaryRef; + CFStringRef vendorKeyCFStringRef; + + CFMutableDictionaryRef productCFMutableDictionaryRef; + CFStringRef productKeyCFStringRef; + + // if the vendor dictionary doesn't exist + vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inVendorID); + if ( CFDictionaryGetValueIfPresent(inCFMutableDictionaryRef, vendorKeyCFStringRef, + (const void **) &vendorCFMutableDictionaryRef) ) + { + // copy it. + vendorCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, vendorCFMutableDictionaryRef); + } else { // ...otherwise... + // create it. + vendorCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + results = TRUE; + } + // if the vendor name key doesn't exist + if ( !CFDictionaryContainsKey(vendorCFMutableDictionaryRef, kNameKeyCFStringRef) ) { + // create it. + CFDictionaryAddValue(vendorCFMutableDictionaryRef, kNameKeyCFStringRef, inVendorCFStringRef); + results = TRUE; + } + + // if the product key exists in the vendor dictionary + productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), inProductID); + if ( CFDictionaryGetValueIfPresent(vendorCFMutableDictionaryRef, productKeyCFStringRef, + (const void **) &productCFMutableDictionaryRef) ) + { + // copy it. + productCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, productCFMutableDictionaryRef); + } else { // ...otherwise... + // create it. + productCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + results = TRUE; + } + // if the product name key doesn't exist + if ( !CFDictionaryContainsKey(productCFMutableDictionaryRef, kNameKeyCFStringRef) ) { + // create it. + CFDictionaryAddValue(productCFMutableDictionaryRef, kNameKeyCFStringRef, inProductCFStringRef); + results = TRUE; + } + if ( vendorCFMutableDictionaryRef ) { + if ( productCFMutableDictionaryRef ) { + if ( results ) { + CFDictionarySetValue(vendorCFMutableDictionaryRef, productKeyCFStringRef, productCFMutableDictionaryRef); + } + + CFRelease(productCFMutableDictionaryRef); + } + if ( results ) { + CFDictionarySetValue(inCFMutableDictionaryRef, vendorKeyCFStringRef, vendorCFMutableDictionaryRef); + } + + CFRelease(vendorCFMutableDictionaryRef); + } + if ( productKeyCFStringRef ) { + CFRelease(productKeyCFStringRef); + } + if ( vendorKeyCFStringRef ) { + CFRelease(vendorKeyCFStringRef); + } + } + + return (results); +} // hu_AddVendorProductToCFDict + +/************************************************************************* + * + * hu_AddDeviceElementToUsageXML( inDevice, inElement ) + * + * Purpose: add a device and it's elements to our usage( XML ) file + * + * Inputs: inDevice - the device + * inElement - the element + * + * Returns: Boolean - if successful + */ +static Boolean hu_AddDeviceElementToUsageXML(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { + Boolean results = FALSE; + if ( gUsageCFPropertyListRef ) { + CFRelease(gUsageCFPropertyListRef); + } + + gUsageCFPropertyListRef = + hu_XMLLoad( CFSTR( + "HID_device_usage_strings"), CFSTR("plist") ); + if ( gUsageCFPropertyListRef ) { + CFMutableDictionaryRef tCFMutableDictionaryRef = + CFDictionaryCreateMutableCopy( + kCFAllocatorDefault, + 0, + gUsageCFPropertyListRef); + if ( tCFMutableDictionaryRef ) { + CFMutableDictionaryRef vendorCFMutableDictionaryRef; + + CFMutableDictionaryRef productCFMutableDictionaryRef; + CFStringRef productKeyCFStringRef; + + CFStringRef usageKeyCFStringRef; + + // if the vendor dictionary exists... + long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); + CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), vendorID); + if ( vendorKeyCFStringRef ) { + if ( CFDictionaryGetValueIfPresent(tCFMutableDictionaryRef, vendorKeyCFStringRef, + (const void **) &vendorCFMutableDictionaryRef) ) + { + // ...copy it... + vendorCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, + 0, + vendorCFMutableDictionaryRef); + } else { // ...otherwise... + // ...create it. + vendorCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + results = TRUE; + } + // if the vendor name key doesn't exist... + if ( !CFDictionaryContainsKey(vendorCFMutableDictionaryRef, kNameKeyCFStringRef) ) { + CFStringRef manCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); + // ...create it. + CFDictionaryAddValue(vendorCFMutableDictionaryRef, kNameKeyCFStringRef, manCFStringRef); + results = TRUE; + } + + // if the product key exists in the vendor dictionary... + long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); + productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), productID); + if ( CFDictionaryGetValueIfPresent(vendorCFMutableDictionaryRef, productKeyCFStringRef, + (const void **) &productCFMutableDictionaryRef) ) + { + // ...copy it... + productCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, + 0, + productCFMutableDictionaryRef); + } else { // ...otherwise... + // ...create it. + productCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + results = TRUE; + } + // if the product name key doesn't exist... + if ( !CFDictionaryContainsKey(productCFMutableDictionaryRef, kNameKeyCFStringRef) ) { + CFStringRef productCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); + // ...create it. + CFDictionaryAddValue(productCFMutableDictionaryRef, kNameKeyCFStringRef, productCFStringRef); + results = TRUE; + } + + // if the usage key doesn't exist in the product dictionary... + uint32_t usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); + uint32_t usage = IOHIDElementGetUsagePage(inIOHIDElementRef); + usageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld:%ld"), usagePage, usage); + if ( usageKeyCFStringRef ) { + if ( !CFDictionaryContainsKey(productCFMutableDictionaryRef, usageKeyCFStringRef) ) { + // find it's generic name + CFStringRef usageCFStringRef = HIDCopyUsageName(usagePage, usage); + if ( usageCFStringRef ) { + // and add that. + CFDictionaryAddValue(productCFMutableDictionaryRef, usageKeyCFStringRef, usageCFStringRef); + results = TRUE; + CFRelease(usageCFStringRef); + } + } + + CFRelease(usageKeyCFStringRef); + } + if ( vendorCFMutableDictionaryRef ) { + if ( productCFMutableDictionaryRef ) { + if ( results ) { + CFDictionarySetValue(vendorCFMutableDictionaryRef, productKeyCFStringRef, productCFMutableDictionaryRef); + } + + CFRelease(productCFMutableDictionaryRef); + } + if ( results ) { + CFDictionarySetValue(tCFMutableDictionaryRef, vendorKeyCFStringRef, vendorCFMutableDictionaryRef); + } + + CFRelease(vendorCFMutableDictionaryRef); + } + + CFRelease(vendorKeyCFStringRef); + } + if ( productKeyCFStringRef ) { + CFRelease(productKeyCFStringRef); + } + if ( results ) { + hu_XMLSave( tCFMutableDictionaryRef, + CFSTR( + "HID_device_usage_strings"), CFSTR("plist") ); + } + + CFRelease( + tCFMutableDictionaryRef); + } + } + + return (results); +} // hu_AddDeviceElementToUsageXML +#endif + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/HID_Queue_Utilities.c b/src/cocoa/HID_Queue_Utilities.c new file mode 100644 index 000000000..ab1d83667 --- /dev/null +++ b/src/cocoa/HID_Queue_Utilities.c @@ -0,0 +1,361 @@ +// File: HID_Queue_Utilities.c +// Abstract: HID Queue Utilities. +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + +#include "HID_Utilities_External.h" + +// ================================== +// private functions + +// creates a queue for a device, creates and opens device interface if required +static IOReturn HIDCreateQueue(IOHIDDeviceRef inIOHIDDeviceRef) { + IOReturn result = kIOReturnSuccess; + if ( inIOHIDDeviceRef ) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + + // do we already have a queue? + IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + if ( tIOHIDQueueRef ) { // (yes) + assert( IOHIDQueueGetTypeID() == CFGetTypeID(tIOHIDQueueRef) ); + } else { + tIOHIDQueueRef = IOHIDQueueCreate(kCFAllocatorDefault, inIOHIDDeviceRef, kDeviceQueueSize, kIOHIDOptionsTypeNone); + if ( tIOHIDQueueRef ) { // did that work + IOHIDDevice_SetQueue(inIOHIDDeviceRef, tIOHIDQueueRef); + result = kIOReturnSuccess; + } else { + HIDReportErrorNum("Failed to create queue via create", result); + } + } + } else { + HIDReportErrorNum("HID device ref does not exist for queue creation", result); + } + + return (result); +} /* HIDCreateQueue */ + +// --------------------------------- +// returns true if queue is empty false otherwise +// error if no device, empty if no queue +static unsigned char HIDIsDeviceQueueEmpty(IOHIDDeviceRef inIOHIDDeviceRef) { + if ( inIOHIDDeviceRef ) { // need device and queue + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + if ( tIOHIDQueueRef ) { + IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO); + + while ( tIOHIDElementRef ) { + if ( IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) { + return (false); + } + + tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO); + } + } else { + HIDReportError("NULL device passed to HIDIsDeviceQueueEmpty."); + } + } else { + HIDReportError("NULL device passed to HIDIsDeviceQueueEmpty."); + } + + return (true); +} /* HIDIsDeviceQueueEmpty */ + +// --------------------------------- + +// disposes and releases queue, sets queue to NULL,. +// Note: will have no effect if device or queue do not exist +static IOReturn HIDDisposeReleaseQueue(IOHIDDeviceRef inIOHIDDeviceRef) { + IOReturn result = kIOReturnSuccess; + if ( inIOHIDDeviceRef ) { + IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + if ( tIOHIDQueueRef ) { + // stop queue + IOHIDQueueStop(tIOHIDQueueRef); + + // release the queue + CFRelease(tIOHIDQueueRef); + } + } else { + HIDReportError("NULL device passed to HIDDisposeReleaseQueue."); + } + + return (result); +} /* HIDDisposeReleaseQueue */ + +// ================================== +// public functions +// ---------------------------------- + +// queues specific element, performing any device queue set up required +// queue is started and ready to return events on exit from this function +int HIDQueueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { + IOReturn result = kIOReturnSuccess; + if ( inIOHIDDeviceRef ) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + if ( inIOHIDElementRef ) { + assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) ); + IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + if ( !tIOHIDQueueRef ) { // if no queue create queue + result = HIDCreateQueue(inIOHIDDeviceRef); + if ( kIOReturnSuccess == result ) { + tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + } + } + if ( tIOHIDQueueRef ) { + // stop queue + IOHIDQueueStop(tIOHIDQueueRef); + // queue element + if ( !IOHIDQueueContainsElement(tIOHIDQueueRef, inIOHIDElementRef) ) { + IOHIDQueueAddElement(tIOHIDQueueRef, inIOHIDElementRef); + } + + // restart queue + IOHIDQueueStart(tIOHIDQueueRef); + } else { + HIDReportError("No queue for device passed to HIDQueueElement."); + if ( kIOReturnSuccess == result ) { + result = kIOReturnError; + } + } + } else { + HIDReportError("NULL element passed to HIDQueueElement."); + result = kIOReturnBadArgument; + } + } else { + HIDReportError("NULL device passed to HIDQueueElement."); + result = kIOReturnBadArgument; + } + + return (result); +} /* HIDQueueElement */ +// --------------------------------- + +// adds all elements to queue, performing any device queue set up required +// queue is started and ready to return events on exit from this function +int HIDQueueDevice(IOHIDDeviceRef inIOHIDDeviceRef) { + IOReturn result = kIOReturnSuccess; + // error checking + if ( !inIOHIDDeviceRef ) { + HIDReportError("Device does not exist, cannot queue device."); + return (kIOReturnBadArgument); + } + if ( !inIOHIDDeviceRef ) { // must have interface + HIDReportError("Device does not have hid device ref, cannot queue device."); + return (kIOReturnError); + } + + IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + if ( !tIOHIDQueueRef ) { // if no queue create queue + result = HIDCreateQueue(inIOHIDDeviceRef); + if ( kIOReturnSuccess == result ) { + tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + } + } + if ( (kIOReturnSuccess != result) || (!tIOHIDQueueRef) ) { + HIDReportErrorNum("Could not queue device due to problem creating queue.", result); + if ( kIOReturnSuccess != result ) { + return (result); + } else { + return (kIOReturnError); + } + } + + // stop queue + IOHIDQueueStop(tIOHIDQueueRef); + + // queue element + IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO); + + while ( tIOHIDElementRef ) { + if ( !IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) { + IOHIDQueueAddElement(tIOHIDQueueRef, tIOHIDElementRef); + } + + tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO); + } + + // restart queue + IOHIDQueueStart(tIOHIDQueueRef); + + return (result); +} /* HIDQueueDevice */ + +// --------------------------------- +// removes element for queue, if last element in queue will release queue and closes device interface +int HIDDequeueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { + IOReturn result = kIOReturnSuccess; + if ( inIOHIDDeviceRef ) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + if ( inIOHIDElementRef ) { + assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) ); + IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + if ( tIOHIDQueueRef ) { + // stop queue + IOHIDQueueStop(tIOHIDQueueRef); + // de-queue element + if ( IOHIDQueueContainsElement(tIOHIDQueueRef, inIOHIDElementRef) ) { + IOHIDQueueRemoveElement(tIOHIDQueueRef, inIOHIDElementRef); + } + // release device queue and close interface if queue empty + if ( HIDIsDeviceQueueEmpty(inIOHIDDeviceRef) ) { + result = HIDDisposeReleaseQueue(inIOHIDDeviceRef); + if ( kIOReturnSuccess != result ) { + HIDReportErrorNum("Failed to dispose and release queue.", result); + } + } else { // not empty so restart queue + IOHIDQueueStart(tIOHIDQueueRef); + } + } else { + HIDReportError("No queue for device passed to HIDDequeueElement."); + if ( kIOReturnSuccess == result ) { + result = kIOReturnError; + } + } + } else { + HIDReportError("NULL element passed to HIDDequeueElement."); + result = kIOReturnBadArgument; + } + } else { + HIDReportError("NULL device passed to HIDDequeueElement."); + result = kIOReturnBadArgument; + } + + return (result); +} /* HIDDequeueElement */ + +// --------------------------------- +// completely removes all elements from queue and releases queue and closes device interface +// does not release device interfaces, application must call ReleaseHIDDeviceList on exit +int HIDDequeueDevice(IOHIDDeviceRef inIOHIDDeviceRef) { + IOReturn result = kIOReturnSuccess; + // error checking + if ( !inIOHIDDeviceRef ) { + HIDReportError("Device does not exist, cannot queue device."); + return (kIOReturnBadArgument); + } + if ( !inIOHIDDeviceRef ) { // must have interface + HIDReportError("Device does not have hid device ref, cannot queue device."); + return (kIOReturnError); + } + + IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + if ( tIOHIDQueueRef ) { + // iterate through elements and if queued, remove + IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO); + + while ( tIOHIDElementRef ) { + // de-queue element + if ( IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) { + IOHIDQueueRemoveElement(tIOHIDQueueRef, tIOHIDElementRef); + } + + tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO); + } + + // ensure queue is disposed and released + result = HIDDisposeReleaseQueue(inIOHIDDeviceRef); + if ( kIOReturnSuccess != result ) { + HIDReportErrorNum("Failed to dispose and release queue.", result); + } + } else { + HIDReportError("No queue for device passed to HIDDequeueElement."); + if ( kIOReturnSuccess == result ) { + result = kIOReturnError; + } + } + + return (result); +} /* HIDDequeueDevice */ +// --------------------------------- + +// releases all device queues for quit or rebuild (must be called) +// does not release device interfaces, application must call ReleaseHIDDeviceList on exit +IOReturn HIDReleaseAllDeviceQueues(void) { + IOReturn result = kIOReturnSuccess; + IOHIDDeviceRef tIOHIDDeviceRef = HIDGetFirstDevice(); + + while ( tIOHIDDeviceRef ) { + result = HIDDequeueDevice(tIOHIDDeviceRef); + if ( kIOReturnSuccess != result ) { + HIDReportErrorNum("Could not dequeue device.", result); + } + + tIOHIDDeviceRef = HIDGetNextDevice(tIOHIDDeviceRef); + } + + return (result); +} /* HIDReleaseAllDeviceQueues */ + +// --------------------------------- +// Get the next event in the queue for a device +// elements or entire device should be queued prior to calling this with HIDQueueElement or HIDQueueDevice +// returns true if an event is avialable for the element and fills out *pHIDEvent structure, returns false otherwise +// Note: kIOReturnUnderrun returned from getNextEvent indicates an empty queue not an error condition +// Note: application should pass in a pointer to a IOHIDEventStruct cast to a void (for CFM compatibility) +unsigned char HIDGetEvent(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDValueRef *pIOHIDValueRef) { + if ( inIOHIDDeviceRef ) { + IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + if ( tIOHIDQueueRef ) { + if ( pIOHIDValueRef ) { + *pIOHIDValueRef = IOHIDQueueCopyNextValueWithTimeout(tIOHIDQueueRef, 0.0); + if ( *pIOHIDValueRef ) { + return (true); + } + } + } else { + HIDReportError("Could not get HID event, hid queue reference does not exist."); + } + } else { + HIDReportError("Could not get HID event, device does not exist."); + } + + return (false); // did not get event +} /* HIDGetEvent */ + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/HID_Utilities.c b/src/cocoa/HID_Utilities.c new file mode 100644 index 000000000..3152cfdd9 --- /dev/null +++ b/src/cocoa/HID_Utilities.c @@ -0,0 +1,1068 @@ +// File: HID_Utilities.c +// Abstract: Implementation of the HID utilities +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//*************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + +#pragma mark - includes & imports +//----------------------------------------------------- + +#include + +#include "HID_Utilities_External.h" + +//*************************************************** +#pragma mark - typedefs, enums, defines, etc. +//----------------------------------------------------- +#define FAKE_MISSING_NAMES 1 // set this to true while debuging to get more explicit element names; false for +// the +// generic ones + +#define kPercentMove 10 // precent of overall range a element must move to register +#define kNameKeyCFStringRef CFSTR("Name") // dictionary key + +//*************************************************** +#pragma mark - local ( static ) function prototypes +//----------------------------------------------------- + +static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context); +static CFComparisonResult CFDeviceArrayComparatorFunction(const void *val1, const void *val2, void *context); +static CFMutableDictionaryRef hu_SetUpMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage); + +//*************************************************** +#pragma mark - exported globals +//----------------------------------------------------- + +IOHIDManagerRef gIOHIDManagerRef = NULL; +CFMutableArrayRef gDeviceCFArrayRef = NULL; +CFIndex gDeviceIndex; +CFArrayRef gElementCFArrayRef = NULL; + +//*************************************************** +#pragma mark - local ( static ) globals +//----------------------------------------------------- + +//*************************************************** +#pragma mark - exported function implementations +//----------------------------------------------------- + +//************************************************************************* +// +// HIDBuildMultiDeviceList( inUsagePages, inUsages, inNumDeviceTypes ) +// +// Purpose: builds list of devices with elements +// +// Inputs: inUsagePages - inNumDeviceTypes sized array of matching usage pages +// inUsages - inNumDeviceTypes sized array of matching usages +// inNumDeviceTypes - number of usage pages & usages +// +// Returns: Boolean - if successful +// +Boolean HIDBuildMultiDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes) { + Boolean result = FALSE; // assume failure ( pessimist! ) + Boolean first = (!gIOHIDManagerRef); // not yet created? + if ( first ) { + // create the manager + gIOHIDManagerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + } + if ( gIOHIDManagerRef ) { + CFMutableArrayRef hidMatchingCFMutableArrayRef = NULL; + if ( inUsages && inUsagePages && inNumDeviceTypes ) { + hidMatchingCFMutableArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + if ( hidMatchingCFMutableArrayRef ) { + int idx; + for ( idx = 0; idx < inNumDeviceTypes; idx++ ) { // for all usage and usage page types + // Set up matching dictionary. returns NULL on error. + CFMutableDictionaryRef hidMatchingCFDictRef = hu_SetUpMatchingDictionary(inUsagePages[idx], inUsages[idx]); + if ( hidMatchingCFDictRef ) { + CFArrayAppendValue(hidMatchingCFMutableArrayRef, (void *) hidMatchingCFDictRef); + CFRelease(hidMatchingCFDictRef); + } else { + fprintf(stderr, "%s: Couldnā€™t create a matching dictionary.", __PRETTY_FUNCTION__); + } + } + } else { + fprintf(stderr, "%s: Couldnā€™t create a matching array.", __PRETTY_FUNCTION__); + } + } + + // set it for IOHIDManager to use to match against + IOHIDManagerSetDeviceMatchingMultiple(gIOHIDManagerRef, hidMatchingCFMutableArrayRef); + if ( hidMatchingCFMutableArrayRef ) { + CFRelease(hidMatchingCFMutableArrayRef); + } + if ( first ) { + // open it + IOReturn tIOReturn = IOHIDManagerOpen(gIOHIDManagerRef, kIOHIDOptionsTypeNone); + if ( kIOReturnSuccess != tIOReturn ) { + fprintf(stderr, "%s: Couldnā€™t open IOHIDManager.", __PRETTY_FUNCTION__); + goto Oops; + } + } + + HIDRebuildDevices(); + result = TRUE; + } else { + fprintf(stderr, "%s: Couldnā€™t create a IOHIDManager.", __PRETTY_FUNCTION__); + } + +Oops: ; + return (result); +} // HIDBuildMultiDeviceList + +/************************************************************************* + * + * HIDBuildDeviceList( inUsagePage, inUsage ) + * + * Purpose: builds list of devices with elements + * + * Notes: same as above but this uses a single inUsagePage and usage + * allocates memory and captures devices + * list is allocated internally within HID Utilites and can be accessed via accessor functions + * structures within list are considered flat and user accessable, but not user modifiable + * can be called again to rebuild list to account for new devices + * ( will do the right thing in case of disposing existing list ) + * + * Inputs: inUsagePage - usage page + * inUsage - usages + * + * Returns: Boolean - if successful + */ + +Boolean HIDBuildDeviceList(UInt32 inUsagePage, UInt32 inUsage) { + return ( HIDBuildMultiDeviceList(&inUsagePage, &inUsage, 1) ); // call HIDBuildMultiDeviceList with a single usage +} + +/************************************************************************* + * + * HIDUpdateDeviceList( inUsagePages, inUsages, inNumDeviceTypes ) + * + * Purpose: updates the current device list for any new/removed devices + * + * Notes: if this is called before HIDBuildDeviceList then it functions like HIDBuildMultiDeviceList + * inUsagePage & inUsage are each a inNumDeviceTypes sized array of matching usage and usage pages + * + * Inputs: inUsagePages - inNumDeviceTypes sized array of matching usage pages + * inUsages - inNumDeviceTypes sized array of matching usages + * inNumDeviceTypes - number of usage pages & usages + * + * Returns: Boolean - TRUE if the device config changed + */ + +Boolean HIDUpdateDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes) { + return ( HIDBuildMultiDeviceList(inUsagePages, inUsages, inNumDeviceTypes) ); +} + +/************************************************************************* + * + * HIDReleaseDeviceList( void ) + * + * Purpose: release list built by above functions + * + * Notes: MUST be called prior to application exit to properly release devices + * if not called( or app crashes ) devices can be recovered by pluging into different location in USB chain + * + * Inputs: none + * + * Returns: none + */ + +void HIDReleaseDeviceList(void) { + if ( gDeviceCFArrayRef ) { + CFRelease(gDeviceCFArrayRef); + gDeviceCFArrayRef = NULL; + } +} // HIDReleaseDeviceList + +/************************************************************************* + * + * HIDHaveDeviceList( void ) + * + * Purpose: does a device list exist? + * + * Inputs: none + * + * Returns: Boolean - TRUE if we have previously built a device list + */ + +Boolean HIDHaveDeviceList(void) { + return (NULL != gDeviceCFArrayRef); +} + +//************************************************************************* +// +// HIDRebuildDevices( ) +// +// Purpose: rebuilds the (internal) list of IOHIDDevices +// +// Inputs: none +// +// Returns: none +// + +void HIDRebuildDevices(void) { + // get the set of devices from the IOHID manager + CFSetRef devCFSetRef = IOHIDManagerCopyDevices(gIOHIDManagerRef); + if ( devCFSetRef ) { + // if the existing array isn't empty... + if ( gDeviceCFArrayRef ) { + // release it + CFRelease(gDeviceCFArrayRef); + } + + // create an empty array + gDeviceCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + // now copy the set to the array + CFSetApplyFunction(devCFSetRef, CFSetApplierFunctionCopyToCFArray, (void *) gDeviceCFArrayRef); + // now sort the array by location ID's + CFIndex cnt = CFArrayGetCount(gDeviceCFArrayRef); + CFArraySortValues(gDeviceCFArrayRef, CFRangeMake(0, cnt), CFDeviceArrayComparatorFunction, NULL); + + // and release the set we copied from the IOHID manager + CFRelease(devCFSetRef); + } +} // HIDRebuildDevices + +// --------------------------------- + +// how many HID devices have been found +// returns 0 if no device list exist + +UInt32 HIDCountDevices(void) { + return ( CFArrayGetCount(gDeviceCFArrayRef) ); +} + +// --------------------------------- + +// how many elements does a specific device have +// returns 0 if device is invlaid or NULL + +UInt32 HIDCountDeviceElements(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask) { + int count = 0; + if ( inIOHIDDeviceRef ) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + + gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); + if ( gElementCFArrayRef ) { + CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef); + for ( idx = 0; idx < cnt; idx++ ) { + IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, idx); + if ( !tIOHIDElementRef ) { + continue; + } + + IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); + + switch ( type ) { + case kIOHIDElementTypeInput_Misc: + case kIOHIDElementTypeInput_Button: + case kIOHIDElementTypeInput_Axis: + case kIOHIDElementTypeInput_ScanCodes: + { + if ( typeMask & kHIDElementTypeInput ) { + count++; + } + + break; + } + + case kIOHIDElementTypeOutput: + { + if ( typeMask & kHIDElementTypeOutput ) { + count++; + } + + break; + } + + case kIOHIDElementTypeFeature: + { + if ( typeMask & kHIDElementTypeFeature ) { + count++; + } + + break; + } + + case kIOHIDElementTypeCollection: + { + if ( typeMask & kHIDElementTypeCollection ) { + count++; + } + + break; + } + default: { + break; + } + } // switch ( type ) + + } // next idx + + CFRelease(gElementCFArrayRef); + gElementCFArrayRef = NULL; + } // if ( gElementCFArrayRef ) + + } // if ( inIOHIDDeviceRef ) + + return (count); +} /* HIDCountDeviceElements */ + +// --------------------------------- + +// get the first device in the device list +// returns NULL if no list exists or it's empty + +IOHIDDeviceRef HIDGetFirstDevice(void) { + IOHIDDeviceRef result = NULL; + + gDeviceIndex = 0; + if ( gDeviceCFArrayRef ) { + CFIndex count = CFArrayGetCount(gDeviceCFArrayRef); + if ( (gDeviceIndex >= 0) && (gDeviceIndex < count) ) { + result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, gDeviceIndex); + } + } + + return (result); +} /* HIDGetFirstDevice */ + +// --------------------------------- + +// get next device in list given current device as parameter +// returns NULL if end of list + +IOHIDDeviceRef HIDGetNextDevice(IOHIDDeviceRef inIOHIDDeviceRef) { + IOHIDDeviceRef result = NULL; + if ( gDeviceCFArrayRef && inIOHIDDeviceRef ) { + CFIndex idx, cnt = CFArrayGetCount(gDeviceCFArrayRef); + // quick case to verify the current device index is valid for current device + if ( (gDeviceIndex >= 0) && (gDeviceIndex < cnt) ) { + result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, gDeviceIndex); + if ( result && (result == inIOHIDDeviceRef) ) { + result = NULL; + gDeviceIndex++; // bump index + } else { + // previous index was invalid; + gDeviceIndex = -1; + // search for current device's index + for ( idx = 0; idx < cnt; idx++ ) { + result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, idx); + if ( (result) && (result == inIOHIDDeviceRef) ) { + gDeviceIndex = idx + 1; // found valid index; bump to next one + break; + } + } + + result = NULL; + } + if ( (gDeviceIndex >= 0) && (gDeviceIndex < cnt) ) { + result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, gDeviceIndex); + } + } // if valid index + + } // if ( gDeviceCFArrayRef && inIOHIDDeviceRef ) + + return (result); +} /* HIDGetNextDevice */ + +// --------------------------------- + +// get the first element of device passed in as parameter +// returns NULL if no list exists or device does not exists or is NULL +IOHIDElementRef HIDGetFirstDeviceElement(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask) { + IOHIDElementRef result = NULL; + if ( inIOHIDDeviceRef ) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + + gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); + if ( gElementCFArrayRef ) { + CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef); + for ( idx = 0; idx < cnt; idx++ ) { + IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, idx); + if ( !tIOHIDElementRef ) { + continue; + } + + IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); + + switch ( type ) { + case kIOHIDElementTypeInput_Misc: + case kIOHIDElementTypeInput_Button: + case kIOHIDElementTypeInput_Axis: + case kIOHIDElementTypeInput_ScanCodes: + { + if ( typeMask & kHIDElementTypeInput ) { + result = tIOHIDElementRef; + } + + break; + } + + case kIOHIDElementTypeOutput: + { + if ( typeMask & kHIDElementTypeOutput ) { + result = tIOHIDElementRef; + } + + break; + } + + case kIOHIDElementTypeFeature: + { + if ( typeMask & kHIDElementTypeFeature ) { + result = tIOHIDElementRef; + } + + break; + } + + case kIOHIDElementTypeCollection: + { + if ( typeMask & kHIDElementTypeCollection ) { + result = tIOHIDElementRef; + } + + break; + } + default: { + break; + } + } // switch ( type ) + if ( result ) { + break; // DONE! + } + } // next idx + + CFRelease(gElementCFArrayRef); + gElementCFArrayRef = NULL; + } // if ( gElementCFArrayRef ) + + } // if ( inIOHIDDeviceRef ) + + return (result); +} /* HIDGetFirstDeviceElement */ + +// --------------------------------- + +// get next element of given device in list given current element as parameter +// will walk down each collection then to next element or collection (depthwise traverse) +// returns NULL if end of list +// uses mask of HIDElementTypeMask to restrict element found +// use kHIDElementTypeIO to get previous HIDGetNextDeviceElement functionality +IOHIDElementRef HIDGetNextDeviceElement(IOHIDElementRef inIOHIDElementRef, HIDElementTypeMask typeMask) { + IOHIDElementRef result = NULL; + if ( inIOHIDElementRef ) { + assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) ); + + IOHIDDeviceRef tIOHIDDeviceRef = IOHIDElementGetDevice(inIOHIDElementRef); + if ( tIOHIDDeviceRef ) { + Boolean found = FALSE; + + gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); + if ( gElementCFArrayRef ) { + CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef); + for ( idx = 0; idx < cnt; idx++ ) { + IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, idx); + if ( !tIOHIDElementRef ) { + continue; + } + if ( !found ) { + if ( inIOHIDElementRef == tIOHIDElementRef ) { + found = TRUE; + } + + continue; // next element + } else { + // we've found the current element; now find the next one of the right type + IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); + + switch ( type ) { + case kIOHIDElementTypeInput_Misc: + case kIOHIDElementTypeInput_Button: + case kIOHIDElementTypeInput_Axis: + case kIOHIDElementTypeInput_ScanCodes: + { + if ( typeMask & kHIDElementTypeInput ) { + result = tIOHIDElementRef; + } + + break; + } + + case kIOHIDElementTypeOutput: + { + if ( typeMask & kHIDElementTypeOutput ) { + result = tIOHIDElementRef; + } + + break; + } + + case kIOHIDElementTypeFeature: + { + if ( typeMask & kHIDElementTypeFeature ) { + result = tIOHIDElementRef; + } + + break; + } + + case kIOHIDElementTypeCollection: + { + if ( typeMask & kHIDElementTypeCollection ) { + result = tIOHIDElementRef; + } + + break; + } + default: { + break; + } + } // switch ( type ) + if ( result ) { + break; // DONE! + } + } // if ( !found ) + + } // next idx + + CFRelease(gElementCFArrayRef); + gElementCFArrayRef = NULL; + } // if ( gElementCFArrayRef ) + + } // if ( inIOHIDDeviceRef ) + + } // if ( inIOHIDElementRef ) + + return (result); +} /* HIDGetNextDeviceElement */ + +#if 0 +// --------------------------------- +// get previous element of given device in list given current element as parameter +// this wlaks directly up the tree to the top element and does not search at each level +// returns NULL if beginning of list +// uses mask of HIDElementTypeMask to restrict element found +// use kHIDElementTypeIO to get non-collection elements +IOHIDElementRef HIDGetPreviousDeviceElement(IOHIDElementRef pElement, HIDElementTypeMask typeMask) { + IOHIDElementRef pPreviousElement = pElement->pPrevious; + + // walk back up tree to element prior + while ( pPreviousElement && !HIDMatchElementTypeMask(pPreviousElement->type, typeMask) ) { + pElement = pPreviousElement; // look at previous element + pPreviousElement = pElement->pPrevious; + } + + return (pPreviousElement); // return this element +} /* HIDGetPreviousDeviceElement */ + +#endif + +// utility routine to dump device info +void HIDDumpDeviceInfo(IOHIDDeviceRef inIOHIDDeviceRef) { + char cstring[256]; + + printf("Device: %p = { ", inIOHIDDeviceRef); + + char manufacturer[256] = ""; // name of manufacturer + CFStringRef tCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); + if ( tCFStringRef ) { + verify( CFStringGetCString(tCFStringRef, manufacturer, sizeof(manufacturer), kCFStringEncodingUTF8) ); + } + + char product[256] = ""; // name of product + tCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); + if ( tCFStringRef ) { + verify( CFStringGetCString(tCFStringRef, product, sizeof(product), kCFStringEncodingUTF8) ); + } + + printf("%s - %s, ", manufacturer, product); + + long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); + if ( vendorID ) { +#if 1 + printf(" vendorID: 0x%04lX, ", vendorID); +#else + if ( HIDGetVendorNameFromVendorID(vendorID, cstring) ) { + printf(" vendorID: 0x%04lX (\"%s\"), ", vendorID, cstring); + } else { + printf(" vendorID: 0x%04lX, ", vendorID); + } + +#endif + } + + long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); + if ( productID ) { +#if 1 + printf(" productID: 0x%04lX, ", productID); +#else + if ( HIDGetProductNameFromVendorProductID(vendorID, productID, cstring) ) { + printf(" productID: 0x%04lX (\"%s\"), ", productID, cstring); + } else { + printf(" productID: 0x%04lX, ", productID); + } + +#endif + } + + uint32_t usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); + uint32_t usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); + if ( !usagePage || !usage ) { + usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef); + usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef); + } + + printf("usage: 0x%04lX:0x%04lX, ", (long unsigned int) usagePage, (long unsigned int) usage); + +#if 1 + tCFStringRef = HIDCopyUsageName(usagePage, usage); + if ( tCFStringRef ) { + verify( CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8) ); + printf("\"%s\", ", cstring); + CFRelease(tCFStringRef); + } + +#endif + +#if 1 + tCFStringRef = IOHIDDevice_GetTransport(inIOHIDDeviceRef); + if ( tCFStringRef ) { + verify( CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8) ); + printf("Transport: \"%s\", ", cstring); + } + + long vendorIDSource = IOHIDDevice_GetVendorIDSource(inIOHIDDeviceRef); + if ( vendorIDSource ) { + printf("VendorIDSource: %ld, ", vendorIDSource); + } + + long version = IOHIDDevice_GetVersionNumber(inIOHIDDeviceRef); + if ( version ) { + printf("version: %ld, ", version); + } + + tCFStringRef = IOHIDDevice_GetSerialNumber(inIOHIDDeviceRef); + if ( tCFStringRef ) { + verify( CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8) ); + printf("SerialNumber: \"%s\", ", cstring); + } + + long country = IOHIDDevice_GetCountryCode(inIOHIDDeviceRef); + if ( country ) { + printf("CountryCode: %ld, ", country); + } + + long locationID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); + if ( locationID ) { + printf("locationID: 0x%08lX, ", locationID); + } + +#if 0 + CFArrayRef pairs = IOHIDDevice_GetUsagePairs(inIOHIDDeviceRef); + if ( pairs ) { + CFIndex idx, cnt = CFArrayGetCount(pairs); + for ( idx = 0; idx < cnt; idx++ ) { + const void *pair = CFArrayGetValueAtIndex(pairs, idx); + CFShow(pair); + } + } + +#endif + long maxInputReportSize = IOHIDDevice_GetMaxInputReportSize(inIOHIDDeviceRef); + if ( maxInputReportSize ) { + printf("MaxInputReportSize: %ld, ", maxInputReportSize); + } + + long maxOutputReportSize = IOHIDDevice_GetMaxOutputReportSize(inIOHIDDeviceRef); + if ( maxOutputReportSize ) { + printf("MaxOutputReportSize: %ld, ", maxOutputReportSize); + } + + long maxFeatureReportSize = IOHIDDevice_GetMaxFeatureReportSize(inIOHIDDeviceRef); + if ( maxFeatureReportSize ) { + printf("MaxFeatureReportSize: %ld, ", maxOutputReportSize); + } + + long reportInterval = IOHIDDevice_GetReportInterval(inIOHIDDeviceRef); + if ( reportInterval ) { + printf("ReportInterval: %ld, ", reportInterval); + } + + IOHIDQueueRef queueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); + if ( queueRef ) { + printf("queue: %p, ", queueRef); + } + + IOHIDTransactionRef transactionRef = IOHIDDevice_GetTransaction(inIOHIDDeviceRef); + if ( transactionRef ) { + printf("transaction: %p, ", transactionRef); + } + +#endif + printf("}\n"); + fflush(stdout); +} // HIDDumpDeviceInfo + +// utility routine to dump element info +void HIDDumpElementInfo(IOHIDElementRef inIOHIDElementRef) { + if ( inIOHIDElementRef ) { + printf(" Element: %p = { ", inIOHIDElementRef); +#if 0 + IOHIDDeviceRef tIOHIDDeviceRef = IOHIDElementGetDevice(inIOHIDElementRef); + printf("Device: %p, ", tIOHIDDeviceRef); +#endif + IOHIDElementRef parentIOHIDElementRef = IOHIDElementGetParent(inIOHIDElementRef); + printf("parent: %p, ", parentIOHIDElementRef); +#if 0 + CFArrayRef childrenCFArrayRef = IOHIDElementGetChildren(inIOHIDElementRef); + printf("children: %p: { ", childrenCFArrayRef); + fflush(stdout); + CFShow(childrenCFArrayRef); + fflush(stdout); + printf(" }, "); +#endif + IOHIDElementCookie tIOHIDElementCookie = IOHIDElementGetCookie(inIOHIDElementRef); + printf("cookie: %p, ", tIOHIDElementCookie); + + IOHIDElementType tIOHIDElementType = IOHIDElementGetType(inIOHIDElementRef); + + switch ( tIOHIDElementType ) { + case kIOHIDElementTypeInput_Misc: + { + printf("type: Misc, "); + break; + } + + case kIOHIDElementTypeInput_Button: + { + printf("type: Button, "); + break; + } + + case kIOHIDElementTypeInput_Axis: + { + printf("type: Axis, "); + break; + } + + case kIOHIDElementTypeInput_ScanCodes: + { + printf("type: ScanCodes, "); + break; + } + + case kIOHIDElementTypeOutput: + { + printf("type: Output, "); + break; + } + + case kIOHIDElementTypeFeature: + { + printf("type: Feature, "); + break; + } + + case kIOHIDElementTypeCollection: + { + IOHIDElementCollectionType tIOHIDElementCollectionType = IOHIDElementGetCollectionType(inIOHIDElementRef); + + switch ( tIOHIDElementCollectionType ) { + case kIOHIDElementCollectionTypePhysical: + { + printf("type: Physical Collection, "); + break; + } + + case kIOHIDElementCollectionTypeApplication: + { + printf("type: Application Collection, "); + break; + } + + case kIOHIDElementCollectionTypeLogical: + { + printf("type: Logical Collection, "); + break; + } + + case kIOHIDElementCollectionTypeReport: + { + printf("type: Report Collection, "); + break; + } + + case kIOHIDElementCollectionTypeNamedArray: + { + printf("type: Named Array Collection, "); + break; + } + + case kIOHIDElementCollectionTypeUsageSwitch: + { + printf("type: Usage Switch Collection, "); + break; + } + + case kIOHIDElementCollectionTypeUsageModifier: + { + printf("type: Usage Modifier Collection, "); + break; + } + + default: + { + printf("type: %p Collection, ", (void *) tIOHIDElementCollectionType); + break; + } + } // switch + + break; + } + + default: + { + printf("type: %p, ", (void *) tIOHIDElementType); + break; + } + } /* switch */ + + uint32_t usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); + uint32_t usage = IOHIDElementGetUsage(inIOHIDElementRef); + printf("usage: 0x%04lX:0x%04lX, ", (long unsigned int) usagePage, (long unsigned int) usage); +#if 1 + CFStringRef tCFStringRef = HIDCopyUsageName(usagePage, usage); + if ( tCFStringRef ) { + char usageString[256] = ""; + verify( CFStringGetCString(tCFStringRef, usageString, sizeof(usageString), kCFStringEncodingUTF8) ); + printf("\"%s\", ", usageString); + CFRelease(tCFStringRef); + } + +#endif + CFStringRef nameCFStringRef = IOHIDElementGetName(inIOHIDElementRef); + char buffer[256]; + if ( nameCFStringRef && CFStringGetCString(nameCFStringRef, buffer, sizeof(buffer), kCFStringEncodingUTF8) ) { + printf("name: %s, ", buffer); + } + + uint32_t reportID = IOHIDElementGetReportID(inIOHIDElementRef); + uint32_t reportSize = IOHIDElementGetReportSize(inIOHIDElementRef); + uint32_t reportCount = IOHIDElementGetReportCount(inIOHIDElementRef); + printf("report: { ID: %lu, Size: %lu, Count: %lu }, ", + (long unsigned int) reportID, (long unsigned int) reportSize, (long unsigned int) reportCount); + + uint32_t unit = IOHIDElementGetUnit(inIOHIDElementRef); + uint32_t unitExp = IOHIDElementGetUnitExponent(inIOHIDElementRef); + if ( unit || unitExp ) { + printf("unit: %lu * 10^%lu, ", (long unsigned int) unit, (long unsigned int) unitExp); + } + + CFIndex logicalMin = IOHIDElementGetLogicalMin(inIOHIDElementRef); + CFIndex logicalMax = IOHIDElementGetLogicalMax(inIOHIDElementRef); + if ( logicalMin != logicalMax ) { + printf("logical: {min: %ld, max: %ld}, ", logicalMin, logicalMax); + } + + CFIndex physicalMin = IOHIDElementGetPhysicalMin(inIOHIDElementRef); + CFIndex physicalMax = IOHIDElementGetPhysicalMax(inIOHIDElementRef); + if ( physicalMin != physicalMax ) { + printf("physical: {min: %ld, max: %ld}, ", physicalMin, physicalMax); + } + + Boolean isVirtual = IOHIDElementIsVirtual(inIOHIDElementRef); + if ( isVirtual ) { + printf("isVirtual, "); + } + + Boolean isRelative = IOHIDElementIsRelative(inIOHIDElementRef); + if ( isRelative ) { + printf("isRelative, "); + } + + Boolean isWrapping = IOHIDElementIsWrapping(inIOHIDElementRef); + if ( isWrapping ) { + printf("isWrapping, "); + } + + Boolean isArray = IOHIDElementIsArray(inIOHIDElementRef); + if ( isArray ) { + printf("isArray, "); + } + + Boolean isNonLinear = IOHIDElementIsNonLinear(inIOHIDElementRef); + if ( isNonLinear ) { + printf("isNonLinear, "); + } + + Boolean hasPreferredState = IOHIDElementHasPreferredState(inIOHIDElementRef); + if ( hasPreferredState ) { + printf("hasPreferredState, "); + } + + Boolean hasNullState = IOHIDElementHasNullState(inIOHIDElementRef); + if ( hasNullState ) { + printf("hasNullState, "); + } + + printf(" }\n"); + } +} // HIDDumpElementInfo + +void HIDDumpElementCalibrationInfo(IOHIDElementRef inIOHIDElementRef) { + printf(" Element: %p = { ", inIOHIDElementRef); + + CFIndex calMin = IOHIDElement_GetCalibrationMin(inIOHIDElementRef); + CFIndex calMax = IOHIDElement_GetCalibrationMax(inIOHIDElementRef); + printf("cal: {min: %ld, max: %ld}, ", calMin, calMax); + + CFIndex satMin = IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef); + CFIndex satMax = IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef); + printf("sat: {min: %ld, max: %ld}, ", satMin, satMax); + + CFIndex deadMin = IOHIDElement_GetCalibrationDeadZoneMin(inIOHIDElementRef); + CFIndex deadMax = IOHIDElement_GetCalibrationDeadZoneMax(inIOHIDElementRef); + printf("dead: {min: %ld, max: %ld}, ", deadMin, deadMax); + + double_t granularity = IOHIDElement_GetCalibrationGranularity(inIOHIDElementRef); + printf("granularity: %6.2f }\n", granularity); +} // HIDDumpElementCalibrationInfo + +//*************************************************** +#pragma mark - local ( static ) function implementations +//----------------------------------------------------- + +//************************************************************************* +// +// CFSetApplierFunctionCopyToCFArray( value, context ) +// +// Purpose: CFSetApplierFunction to copy the CFSet to a CFArray +// +// Notes: called one time for each item in the CFSet +// +// Inputs: value - the current element of the CFSet +// context - the CFMutableArrayRef we're adding the CFSet elements to +// +// Returns: nothing +// +static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context) { + // printf( "%s: 0x%08lX\n", __PRETTY_FUNCTION__, (long unsigned int) value ); + CFArrayAppendValue( (CFMutableArrayRef) context, value ); +} // CFSetApplierFunctionCopyToCFArray + +// --------------------------------- +// used to sort the CFDevice array after copying it from the (unordered) (CF)set. +// we compare based on the location ID's since they're consistant (across boots & launches). +// +static CFComparisonResult CFDeviceArrayComparatorFunction(const void *val1, const void *val2, void *context) { +#pragma unused( context ) + CFComparisonResult result = kCFCompareEqualTo; + + long loc1 = IOHIDDevice_GetLocationID( (IOHIDDeviceRef) val1 ); + long loc2 = IOHIDDevice_GetLocationID( (IOHIDDeviceRef) val2 ); + if ( loc1 < loc2 ) { + result = kCFCompareLessThan; + } else if ( loc1 > loc2 ) { + result = kCFCompareGreaterThan; + } + + return (result); +} // CFDeviceArrayComparatorFunction + +//************************************************************************* +// +// hu_SetUpMatchingDictionary( inUsagePage, inUsage ) +// +// Purpose: builds a matching dictionary based on usage page and usage +// +// Notes: Only called by HIDBuildMultiDeviceList +// +// Inputs: inUsagePage - usage page +// inUsage - usages +// +// Returns: CFMutableDictionaryRef - the matching dictionary +// + +static CFMutableDictionaryRef hu_SetUpMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage) { + // create a dictionary to add usage page/usages to + CFMutableDictionaryRef refHIDMatchDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if ( refHIDMatchDictionary ) { + if ( inUsagePage ) { + // Add key for device type to refine the matching dictionary. + CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage); + if ( pageCFNumberRef ) { + CFDictionarySetValue(refHIDMatchDictionary, + CFSTR(kIOHIDPrimaryUsagePageKey), pageCFNumberRef); + CFRelease(pageCFNumberRef); + // note: the usage is only valid if the usage page is also defined + if ( inUsage ) { + CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage); + if ( usageCFNumberRef ) { + CFDictionarySetValue(refHIDMatchDictionary, + CFSTR(kIOHIDPrimaryUsageKey), usageCFNumberRef); + CFRelease(usageCFNumberRef); + } else { + fprintf(stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__); + } + } + } else { + fprintf(stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__); + } + } + } else { + fprintf(stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__); + } + + return (refHIDMatchDictionary); +} // hu_SetUpMatchingDictionary + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/HID_Utilities_External.h b/src/cocoa/HID_Utilities_External.h new file mode 100644 index 000000000..bd498bc51 --- /dev/null +++ b/src/cocoa/HID_Utilities_External.h @@ -0,0 +1,417 @@ +// File: HID_Utilities_External.h +// Abstract: External interface for HID Utilities, can be used with either library or source. +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** +#ifndef _HID_Utilities_External_h_ +#define _HID_Utilities_External_h_ + +// ================================== + +#ifdef __cplusplus +extern "C" { +#endif + +// ================================== + +//includes + +#include +#include "IOHIDLib_.h" + +// ================================== + +#ifndef _IOKIT_HID_IOHIDKEYS_H_ +/*! + @typedef IOHIDElementCookie + @abstract Abstract data type used as a unique identifier for an element. + */ +#ifdef __LP64__ +typedef uint32_t IOHIDElementCookie; +#else +typedef void *IOHIDElementCookie; +#endif +#endif + +// Device and Element Interfaces + +enum HIDElementTypeMask { + kHIDElementTypeInput = 1 << 1, + kHIDElementTypeOutput = 1 << 2, + kHIDElementTypeFeature = 1 << 3, + kHIDElementTypeCollection = 1 << 4, + kHIDElementTypeIO = kHIDElementTypeInput | kHIDElementTypeOutput | kHIDElementTypeFeature, + kHIDElementTypeAll = kHIDElementTypeIO | kHIDElementTypeCollection +}; +typedef enum HIDElementTypeMask HIDElementTypeMask; + +// ================================== + +//***************************************************** +#pragma mark - exported globals +//----------------------------------------------------- + +extern IOHIDManagerRef gIOHIDManagerRef; +extern CFMutableArrayRef gDeviceCFArrayRef; +extern CFArrayRef gElementCFArrayRef; + +//************************************************************************* +// +// HIDBuildMultiDeviceList( inUsagePages, inUsages, inNumDeviceTypes ) +// +// Purpose: builds list of devices with elements (allocates memory and captures devices) in which +// the devices could be of different types/usages list is allocated internally within HID +// Utilites and can be accessed via accessor functions structures within list are considered +// flat and user accessable, but not user modifiable can be called again to rebuild list to +// account for new devices (will do the right thing in case of disposing existing list) +// usagePage, usage are each a numDeviceTypes sized array of matching usage and usage pages +// returns true if succesful +// +// Inputs: inUsagePages - inNumDeviceTypes sized array of matching usage pages +// inUsages - inNumDeviceTypes sized array of matching usages +// inNumDeviceTypes - number of usage pages & usages +// +// Returns: Boolean - if successful +// +extern Boolean HIDBuildMultiDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes); + +// same as above but this uses a single usagePage and usage +extern Boolean HIDBuildDeviceList(UInt32 usagePage, UInt32 usage); + +// updates the current device list for any new/removed devices +// if this is called before HIDBuildDeviceList the it functions like HIDBuildMultiDeviceList +// usagePage, usage are each a numDeviceTypes sized array of matching usage and usage pages +// returns true if successful which means if any device were added or removed (the device config changed) +extern Boolean HIDUpdateDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes); + +// release list built by above function +// MUST be called prior to application exit to properly release devices +// if not called (or app crashes) devices can be recovered by pluging into different location in USB chain +extern void HIDReleaseDeviceList(void); + +//************************************************************************* +// +// HIDRebuildDevices( ) +// +// Purpose: rebuilds the (internal) list of devices +// +// Inputs: none +// +// Returns: none +// + +extern void HIDRebuildDevices(void); + +// does a device list exist +extern unsigned char HIDHaveDeviceList(void); + +// how many HID devices have been found +// returns 0 if no device list exist +extern UInt32 HIDCountDevices(void); + +// how many elements does a specific device have +// returns 0 if device is invalid or NULL +// uses mask of HIDElementTypeMask to restrict element found +// use kHIDElementTypeIO to get non-collection elements +extern UInt32 HIDCountDeviceElements(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask); + +// get the first device in the device list +// returns NULL if no list exists +extern IOHIDDeviceRef HIDGetFirstDevice(void); + +// get next device in list given current device as parameter +// returns NULL if end of list +extern IOHIDDeviceRef HIDGetNextDevice(IOHIDDeviceRef inIOHIDDeviceRef); + +// get the first element of device passed in as parameter +// returns NULL if no list exists or device does not exists or is NULL +// uses mask of HIDElementTypeMask to restrict element found +// use kHIDElementTypeIO to get previous HIDGetFirstDeviceElement functionality +extern IOHIDElementRef HIDGetFirstDeviceElement(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask); + +// get next element of given device in list given current element as parameter +// will walk down each collection then to next element or collection (depthwise traverse) +// returns NULL if end of list +// uses mask of HIDElementTypeMask to restrict element found +// use kHIDElementTypeIO to get previous HIDGetNextDeviceElement functionality +extern IOHIDElementRef HIDGetNextDeviceElement(IOHIDElementRef inIOHidElementRef, HIDElementTypeMask typeMask); + +// get previous element of given device in list given current element as parameter +// this walks directly up the tree to the top element and does not search at each level +// returns NULL if beginning of list +// uses mask of HIDElementTypeMask to restrict element found +// use kHIDElementTypeIO to get non-collection elements +extern IOHIDElementRef HIDGetPreviousDeviceElement(IOHIDElementRef inIOHidElementRef, HIDElementTypeMask typeMask); + +// returns C string type name given a type enumeration passed in as parameter( see IOHIDKeys.h ) +// returns empty string for invalid types +extern void HIDGetTypeName(IOHIDElementType inIOHIDElementType, char *outCStrName); + +//************************************************************************* +// +// HIDCopyUsageName( inUsagePage, inUsage ) +// +// Purpose: return a CFStringRef string for a given usage page & usage( see IOUSBHIDParser.h ) +// +// Notes: returns usage page and usage values in CFString form for unknown values +// +// Inputs: inUsagePage - the usage page +// inUsage - the usage +// +// Returns: CFStringRef - the resultant string +// + +extern CFStringRef HIDCopyUsageName(long inUsagePage, long inUsage); + +// ================================== + +// Element Event Queue and Value Interfaces + +enum { + kDefaultUserMin = 0, // default user min and max used for scaling + kDefaultUserMax = 255 +}; + +enum { + kDeviceQueueSize = 50 // this is wired kernel memory so should be set to as small as possible + // but should account for the maximum possible events in the queue + // USB updates will likely occur at 100 Hz so one must account for this rate of + // if states change quickly (updates are only posted on state changes) +}; + +// ================================== + +// queues specific element, performing any device queue set up required +extern int HIDQueueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHidElementRef); + +// adds all elements to queue, performing any device queue set up required +extern int HIDQueueDevice(IOHIDDeviceRef inIOHIDDeviceRef); + +// removes element for queue, if last element in queue will release queue and device +extern int HIDDequeueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHidElementRef); + +// completely removes all elements from queue and releases queue and device +extern int HIDDequeueDevice(IOHIDDeviceRef inIOHIDDeviceRef); + +// releases all device queues for quit or rebuild (must be called) +extern int HIDReleaseAllDeviceQueues(void); + +// returns true if an event is avialable for the element and fills out *pHIDEvent structure, returns false otherwise +// pHIDEvent is a poiner to a IOHIDEventStruct, using void here for compatibility, users can cast a required +extern unsigned char HIDGetEvent(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDValueRef *pIOHIDValueRef); + +// ================================== + +// Conguration and Save Interfaces + +enum { + kPercentMove = 10 // precent of overall range a element must move to register +}; + +typedef struct HID_info_struct { + int actionCookie; + // device + // need to add serial number when I have a test case + struct { + int vendorID, productID; + int locID; + uint32_t usagePage, usage; + } device; + // elements + struct { + uint32_t usagePage, usage; + int minReport, maxReport; + IOHIDElementCookie cookie; // always 32 bits + } element; +}HID_info_rec, *HID_info_ptr; + +// get vendor name from vendor ID +extern Boolean HIDGetVendorNameFromVendorID(long inVendorID, char *outCStrName); + +// get product name from vendor/product ID +extern Boolean HIDGetProductNameFromVendorProductID(long inVendorID, long inProductID, char *outCStrName); + +// get element name from vendor id/product id look up ( using element cookie ) +extern Boolean HIDGetElementNameFromVendorProductCookie(int inVendorID, + int inProductID, + IOHIDElementCookie inCookie, + char * outCStrName); + +// get element name from vendor id/product id look up ( using element usage page & usage ) +extern Boolean HIDGetElementNameFromVendorProductUsage(long inVendorID, + long inProductID, + long inUsagePage, + long inUsage, + char *inCStrName); + +// utility routines to dump device or element info +extern void HIDDumpDeviceInfo(IOHIDDeviceRef inIOHIDDeviceRef); +extern void HIDDumpElementInfo(IOHIDElementRef inIOHIDElementRef); +extern void HIDDumpElementCalibrationInfo(IOHIDElementRef inIOHIDElementRef); + +// polls single device's elements for a change greater than kPercentMove. Times out after given time +// returns 1 and pointer to element if found +// returns 0 and NULL for both parameters if not found +extern unsigned char HIDConfigureSingleDeviceAction(IOHIDDeviceRef inIOHIDDeviceRef, + IOHIDElementRef *outIOHIDElementRef, + float timeout); + +//************************************************************************* +// +// HIDConfigureAction( outDeviceRef, outElementRef, inTimeout ) +// +// Purpose: polls all devices and elements for a change greater than kPercentMove. +// Times out after given time returns 1 and pointer to device and element +// if found; returns 0 and NULL for both parameters if not found +// +// Inputs: outDeviceRef - address where to store the device +// outElementRef - address where to store the element +// inTimeout - the timeout +// Returns: Boolean - TRUE if successful +// outDeviceRef - the device +// outElementRef - the element +// + +extern Boolean HIDConfigureAction(IOHIDDeviceRef *outDeviceRef, IOHIDElementRef *outElementRef, float inTimeout); + +//************************************************************************* +// +// HIDSaveElementPref( inKeyCFStringRef, inAppCFStringRef, inDeviceRef, inElementRef ) +// +// Purpose: Save the device & element values into the specified key in the specified applications preferences +// +// Inputs: inKeyCFStringRef - the preference key +// inAppCFStringRef - the application identifier +// inDeviceRef - the device +// inElementRef - the element +// Returns: Boolean - if successful +// + +extern Boolean HIDSaveElementPref(const CFStringRef inKeyCFStringRef, + CFStringRef inAppCFStringRef, + IOHIDDeviceRef inDeviceRef, + IOHIDElementRef inElementRef); + +//************************************************************************* +// +// HIDRestoreElementPref( inKeyCFStringRef, inAppCFStringRef, outDeviceRef, outElementRef ) +// +// Purpose: Find the specified preference in the specified application +// +// Inputs: inKeyCFStringRef - the preference key +// inAppCFStringRef - the application identifier +// outDeviceRef - address where to restore the device +// outElementRef - address where to restore the element +// Returns: Boolean - if successful +// outDeviceRef - the device +// outElementRef - the element +// + +extern Boolean HIDRestoreElementPref(CFStringRef inKeyCFStringRef, + CFStringRef inAppCFStringRef, + IOHIDDeviceRef * outDeviceRef, + IOHIDElementRef *outElementRef); + +//************************************************************************* +// +// HIDFindDeviceAndElement( inSearchInfo, outFoundDevice, outFoundElement ) +// +// Purpose: find the closest matching device and element for this action +// +// Notes: matches device: serial, vendorID, productID, location, inUsagePage, usage +// matches element: cookie, inUsagePage, usage, +// +// Inputs: inSearchInfo - the device & element info we searching for +// outFoundDevice - the address of the best matching device +// outFoundElement - the address of the best matching element +// +// Returns: Boolean - TRUE if we find a match +// outFoundDevice - the best matching device +// outFoundElement - the best matching element +// + +extern Boolean HIDFindDeviceAndElement(const HID_info_rec *inSearchInfo, + IOHIDDeviceRef * outFoundDevice, + IOHIDElementRef * outFoundElement); + +// -- These are routines to use if the applcationwants HID Utilities to do the file handling -- +// Note: the FILE * is a MachO posix FILE and will not likely work directly with MW MSL FILE * type. + +// take input records, save required info +// assume file is open and at correct position. +void HIDSaveElementConfig(FILE *fileRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHidElementRef, int actionCookie); + +// takes a file, reads one record (assume file position is correct and file is open) +// search for matching device +// return tIOHIDDeviceRef, tIOHIDElementRef and cookie for action +int HIDRestoreElementConfig(FILE *fileRef, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef); + +// -- These are routines to use if the client wants to use their own file handling -- + +// Set up a config record for saving +// takes an input records, returns record user can save as they want +// Note: the save rec must be pre-allocated by the calling app and will be filled out +void HIDSetElementConfig(HID_info_ptr inHIDInfoPtr, + IOHIDDeviceRef inIOHIDDeviceRef, + IOHIDElementRef inIOHidElementRef, + int actionCookie); + +// Get matching element from config record +// takes a pre-allocated and filled out config record +// search for matching device +// return tIOHIDDeviceRef, tIOHIDElementRef and cookie for action +int HIDGetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef); + +// ================================== + +// Error reporter, can be set to report however the application desires +extern void HIDReportError(const char *strError); + +// Error with numeric code reporter, can be set to report however the application desires +extern void HIDReportErrorNum(const char *strError, int numError); + +#ifdef __cplusplus +} +#endif + +#endif // _HID_Utilities_External_h_ diff --git a/src/cocoa/IOHIDDevice_.c b/src/cocoa/IOHIDDevice_.c new file mode 100644 index 000000000..30c01dc7c --- /dev/null +++ b/src/cocoa/IOHIDDevice_.c @@ -0,0 +1,619 @@ +// File: IOHIDDevice_.c +// Abstract: convieance functions for IOHIDDeviceGetProperty +// Version: 2.0 + 5.3 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + +#pragma mark - includes & imports +//----------------------------------------------------- + +#include "IOHIDDevice_.h" + +//***************************************************** +#pragma mark - typedef's, struct's, enums, defines, etc. +//----------------------------------------------------- + +#define kIOHIDDevice_TransactionKey "DeviceTransactionRef" +#define kIOHIDDevice_QueueKey "DeviceQueueRef" + +//***************************************************** +#pragma mark - local (static) function prototypes +//----------------------------------------------------- + +static Boolean IOHIDDevice_GetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, + CFStringRef inKey, + uint32_t * outValue); +// static void IOHIDDevice_SetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t inValue); + +static Boolean IOHIDDevice_GetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, + CFStringRef inKey, + void ** outValue); +static void IOHIDDevice_SetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, + CFStringRef inKey, + void * inValue); + +//***************************************************** +#pragma mark - exported globals +//----------------------------------------------------- + +//***************************************************** +#pragma mark - local (static) globals +//----------------------------------------------------- + +//***************************************************** +#pragma mark - exported function implementations +//----------------------------------------------------- + +//************************************************************************* +// +// HIDIsValidDevice( inIOHIDDeviceRef ) +// +// Purpose: validate this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: Boolean - TRUE if we find the device in our( internal ) device list +// + +Boolean HIDIsValidDevice(IOHIDDeviceRef inIOHIDDeviceRef) { + Boolean result = FALSE; // assume failure (pessimist!) + if ( inIOHIDDeviceRef ) { + if ( CFGetTypeID(inIOHIDDeviceRef) ==IOHIDDeviceGetTypeID() ) { + result = TRUE; + } + } + + return (result); +} // HIDIsValidDevice + +//************************************************************************* +// +// IOHIDDevice_GetTransport( inIOHIDDeviceRef ) +// +// Purpose: get the Transport CFString for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: CFStringRef - the Transport for this device +// + +CFStringRef IOHIDDevice_GetTransport(IOHIDDeviceRef inIOHIDDeviceRef) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDTransportKey) ) ); +} + +//************************************************************************* +// +// IOHIDDevice_GetVendorID( inIOHIDDeviceRef ) +// +// Purpose: get the vendor ID for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the vendor ID for this device +// + +uint32_t IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDKey), &result); + return (result); +} // IOHIDDevice_GetVendorID + +//************************************************************************* +// +// IOHIDDevice_GetVendorIDSource( inIOHIDDeviceRef ) +// +// Purpose: get the VendorIDSource for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the VendorIDSource for this device +// + +uint32_t IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDSourceKey), &result); + return (result); +} // IOHIDDevice_GetVendorIDSource + +//************************************************************************* +// +// IOHIDDevice_GetProductID( inIOHIDDeviceRef ) +// +// Purpose: get the product ID for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the product ID for this device +// + +uint32_t IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDProductIDKey), &result); + return (result); +} // IOHIDDevice_GetProductID + +//************************************************************************* +// +// IOHIDDevice_GetVersionNumber( inIOHIDDeviceRef ) +// +// Purpose: get the VersionNumber CFString for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the VersionNumber for this device +// + +uint32_t IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVersionNumberKey), &result); + return (result); +} // IOHIDDevice_GetVersionNumber + +//************************************************************************* +// +// IOHIDDevice_GetManufacturer( inIOHIDDeviceRef ) +// +// Purpose: get the Manufacturer CFString for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: CFStringRef - the Manufacturer for this device +// + +CFStringRef IOHIDDevice_GetManufacturer(IOHIDDeviceRef inIOHIDDeviceRef) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDManufacturerKey) ) ); +} // IOHIDDevice_GetManufacturer + +//************************************************************************* +// +// IOHIDDevice_GetProduct( inIOHIDDeviceRef ) +// +// Purpose: get the Product CFString for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: CFStringRef - the Product for this device +// + +CFStringRef IOHIDDevice_GetProduct(IOHIDDeviceRef inIOHIDDeviceRef) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDProductKey) ) ); +} // IOHIDDevice_GetProduct + +//************************************************************************* +// +// IOHIDDevice_GetSerialNumber( inIOHIDDeviceRef ) +// +// Purpose: get the SerialNumber CFString for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: CFStringRef - the SerialNumber for this device +// + +CFStringRef IOHIDDevice_GetSerialNumber(IOHIDDeviceRef inIOHIDDeviceRef) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDSerialNumberKey) ) ); +} + +//************************************************************************* +// +// IOHIDDevice_GetCountryCode( inIOHIDDeviceRef ) +// +// Purpose: get the CountryCode CFString for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the CountryCode for this device +// + +uint32_t IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDCountryCodeKey), &result); + return (result); +} // IOHIDDevice_GetCountryCode + +//************************************************************************* +// +// IOHIDDevice_GetLocationID( inIOHIDDeviceRef ) +// +// Purpose: get the location ID for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the location ID for this device +// + +uint32_t IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey), &result); + return (result); +} // IOHIDDevice_GetLocationID + +//************************************************************************* +// +// IOHIDDevice_GetUsage( inIOHIDDeviceRef ) +// +// Purpose: get the usage for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the usage for this device +// + +uint32_t IOHIDDevice_GetUsage(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsageKey), &result); + return (result); +} // IOHIDDevice_GetUsage + +//************************************************************************* +// +// IOHIDDevice_GetUsagePage( inIOHIDDeviceRef ) +// +// Purpose: get the usage page for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the usage page for this device +// + +uint32_t IOHIDDevice_GetUsagePage(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsagePageKey), &result); + return (result); +} // IOHIDDevice_GetUsagePage + +//************************************************************************* +// +// IOHIDDevice_GetUsagePairs( inIOHIDDeviceRef ) +// +// Purpose: get the UsagePairs CFString for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: CFArrayRef - the UsagePairs for this device +// + +CFArrayRef IOHIDDevice_GetUsagePairs(IOHIDDeviceRef inIOHIDDeviceRef) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsagePairsKey) ) ); +} + +//************************************************************************* +// +// IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef ) +// +// Purpose: get the PrimaryUsage CFString for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the PrimaryUsage for this device +// + +uint32_t IOHIDDevice_GetPrimaryUsage(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey), &result); + return (result); +} // IOHIDDevice_GetPrimaryUsage + +//************************************************************************* +// +// IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef ) +// +// Purpose: get the PrimaryUsagePage CFString for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the PrimaryUsagePage for this device +// + +uint32_t IOHIDDevice_GetPrimaryUsagePage(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey), &result); + return (result); +} // IOHIDDevice_GetPrimaryUsagePage + +//************************************************************************* +// +// IOHIDDevice_GetMaxInputReportSize( inIOHIDDeviceRef ) +// +// Purpose: get the MaxInputReportSize CFString for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the MaxInputReportSize for this device +// + +uint32_t IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxInputReportSizeKey), &result); + return (result); +} // IOHIDDevice_GetMaxInputReportSize + +//************************************************************************* +// +// IOHIDDevice_GetMaxOutputReportSize( inIOHIDDeviceRef ) +// +// Purpose: get the MaxOutputReportSize for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the MaxOutput for this device +// + +uint32_t IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxOutputReportSizeKey), &result); + return (result); +} // IOHIDDevice_GetMaxOutputReportSize + +//************************************************************************* +// +// IOHIDDevice_GetMaxFeatureReportSize( inIOHIDDeviceRef ) +// +// Purpose: get the MaxFeatureReportSize for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the MaxFeatureReportSize for this device +// + +uint32_t IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxFeatureReportSizeKey), &result); + return (result); +} // IOHIDDevice_GetMaxFeatureReportSize + +//************************************************************************* +// +// IOHIDDevice_GetReportInterval( inIOHIDDeviceRef ) +// +// Purpose: get the ReportInterval for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: uint32_t - the ReportInterval for this device +// +#ifndef kIOHIDReportIntervalKey +#define kIOHIDReportIntervalKey "ReportInterval" +#endif +uint32_t IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef) { + uint32_t result = 0; + (void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDReportIntervalKey), &result); + return (result); +} // IOHIDDevice_GetReportInterval + +//************************************************************************* +// +// IOHIDDevice_GetQueue( inIOHIDDeviceRef ) +// +// Purpose: get the Queue for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: IOHIDQueueRef - the Queue for this device +// + +IOHIDQueueRef IOHIDDevice_GetQueue(IOHIDDeviceRef inIOHIDDeviceRef) { + IOHIDQueueRef result = 0; + (void) IOHIDDevice_GetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), (void *) &result); + if ( result ) { + assert( IOHIDQueueGetTypeID() == CFGetTypeID(result) ); + } + + return (result); +} // IOHIDDevice_GetQueue + +//************************************************************************* +// +// IOHIDDevice_SetQueue( inIOHIDDeviceRef, inQueueRef ) +// +// Purpose: Set the Queue for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// inQueueRef - the Queue reference +// +// Returns: nothing +// + +void IOHIDDevice_SetQueue(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDQueueRef inQueueRef) { + IOHIDDevice_SetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), inQueueRef); +} + +//************************************************************************* +// +// IOHIDDevice_GetTransaction( inIOHIDDeviceRef ) +// +// Purpose: get the Transaction for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// +// Returns: IOHIDTransactionRef - the Transaction for this device +// + +IOHIDTransactionRef IOHIDDevice_GetTransaction(IOHIDDeviceRef inIOHIDDeviceRef) { + IOHIDTransactionRef result = 0; + (void) IOHIDDevice_GetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), (void *) &result); + return (result); +} // IOHIDDevice_GetTransaction + +//************************************************************************* +// +// IOHIDDevice_SetTransaction( inIOHIDDeviceRef, inTransactionRef ) +// +// Purpose: Set the Transaction for this device +// +// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device +// inTransactionRef - the Transaction reference +// +// Returns: nothing +// + +void IOHIDDevice_SetTransaction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDTransactionRef inTransactionRef) { + IOHIDDevice_SetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), inTransactionRef); +} + +//***************************************************** +#pragma mark - local (static) function implementations +//----------------------------------------------------- + +//************************************************************************* +// +// IOHIDDevice_GetUInt32Property( inIOHIDDeviceRef, inKey, outValue ) +// +// Purpose: convieance function to return a uint32_t property of a device +// +// Inputs: inIOHIDDeviceRef - the device +// inKey - CFString for the +// outValue - address where to restore the element +// Returns: the action cookie +// outValue - the device +// + +static Boolean IOHIDDevice_GetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t *outValue) { + Boolean result = FALSE; + if ( inIOHIDDeviceRef ) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + + CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey); + if ( tCFTypeRef ) { + // if this is a number + if ( CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef) ) { + // get it's value + result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue ); + } + } + } + + return (result); +} // IOHIDDevice_GetUInt32Property + +//************************************************************************* +// +// IOHIDDevice_SetUInt32Property( inIOHIDDeviceRef, inKey, inValue ) +// +// Purpose: convieance function to set a long property of an Device +// +// Inputs: inIOHIDDeviceRef - the Device +// inKey - CFString for the key +// inValue - the value to set it to +// Returns: nothing +// +#if 0 // unused +static void IOHIDDevice_SetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t inValue) { + CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue); + if ( tCFNumberRef ) { + IOHIDDeviceSetProperty(inIOHIDDeviceRef, inKey, tCFNumberRef); + CFRelease(tCFNumberRef); + } +} // IOHIDDevice_SetUInt32Property +#endif +//************************************************************************* +// +// IOHIDDevice_GetPtrProperty( inIOHIDDeviceRef, inKey, outValue ) +// +// Purpose: convieance function to return a pointer property of a device +// +// Inputs: inIOHIDDeviceRef - the device +// inKey - CFString for the +// outValue - address where to restore the element +// Returns: the action cookie +// outValue - the device +// + +static Boolean IOHIDDevice_GetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, void **outValue) { + Boolean result = FALSE; + if ( inIOHIDDeviceRef ) { + assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); + + CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey); + if ( tCFTypeRef ) { + // if this is a number + if ( CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef) ) { + // get it's value +#ifdef __LP64__ + result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt64Type, outValue ); +#else + result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue ); +#endif // ifdef __LP64__ + } + } + } + + return (result); +} // IOHIDDevice_GetPtrProperty + +//************************************************************************* +// +// IOHIDDevice_SetPtrProperty( inIOHIDDeviceRef, inKey, inValue ) +// +// Purpose: convieance function to set a long property of an Device +// +// Inputs: inIOHIDDeviceRef - the Device +// inKey - CFString for the key +// inValue - the value to set it to +// Returns: nothing +// + +static void IOHIDDevice_SetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, void *inValue) { +#ifdef __LP64__ + CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &inValue); +#else + CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue); +#endif // ifdef __LP64__ + if ( tCFNumberRef ) { + IOHIDDeviceSetProperty(inIOHIDDeviceRef, inKey, tCFNumberRef); + CFRelease(tCFNumberRef); + } +} // IOHIDDevice_SetPtrProperty + +//***************************************************** + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/IOHIDDevice_.h b/src/cocoa/IOHIDDevice_.h new file mode 100644 index 000000000..eebccd667 --- /dev/null +++ b/src/cocoa/IOHIDDevice_.h @@ -0,0 +1,422 @@ +// File: IOHIDDevice_.h +// Abstract: convieance functions for IOHIDDeviceGetProperty +// Version: 2.0 + 5.3 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2014 Apple Inc. All Rights Reserved. +// +//***************************************************** +#ifndef __IOHIDDevice__ +#define __IOHIDDevice__ + +//***************************************************** +#pragma mark - includes & imports + +#include + +#include "IOHIDLib_.h" + +//***************************************************** +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN +#pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH +#pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK +#pragma pack(2) +#endif + + //***************************************************** +#pragma mark - typedef's, struct's, enums, defines, etc. + //----------------------------------------------------- + + //***************************************************** +#pragma mark - exported globals + //----------------------------------------------------- + + //***************************************************** +#pragma mark - exported function prototypes + //----------------------------------------------------- + + //************************************************************************* + // + // HIDIsValidDevice( inIOHIDDeviceRef ) + // + // Purpose: validate this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: Boolean - TRUE if we find the device in our( internal ) device list + // + + extern Boolean HIDIsValidDevice(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetTransport( inIOHIDDeviceRef ) + // + // Purpose: get the Transport CFString for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: CFStringRef - the Transport CFString for this device + // + + extern CFStringRef IOHIDDevice_GetTransport(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetVendorID( inIOHIDDeviceRef ) + // + // Purpose: get the vendor ID for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the vendor ID for this device + // + + extern uint32_t IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetVendorIDSource( inIOHIDDeviceRef ) + // + // Purpose: get the VendorIDSource for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the VendorIDSource for this device + // + + extern uint32_t IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetProductID( inIOHIDDeviceRef ) + // + // Purpose: get the product ID for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the product ID for this device + // + + extern uint32_t IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetVersionNumber( inIOHIDDeviceRef ) + // + // Purpose: get the VersionNumber CFString for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the VersionNumber for this device + // + + extern uint32_t IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetManufacturer( inIOHIDDeviceRef ) + // + // Purpose: get the Manufacturer CFString for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: CFStringRef - the Manufacturer CFString for this device + // + + extern CFStringRef IOHIDDevice_GetManufacturer(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetProduct( inIOHIDDeviceRef ) + // + // Purpose: get the Product CFString for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: CFStringRef - the Product CFString for this device + // + + extern CFStringRef IOHIDDevice_GetProduct(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetSerialNumber( inIOHIDDeviceRef ) + // + // Purpose: get the SerialNumber CFString for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: CFStringRef - the SerialNumber CFString for this device + // + + extern CFStringRef IOHIDDevice_GetSerialNumber(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetCountryCode( inIOHIDDeviceRef ) + // + // Purpose: get the CountryCode CFString for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the CountryCode for this device + // + + extern uint32_t IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetLocationID( inIOHIDDeviceRef ) + // + // Purpose: get the location ID for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the location ID for this device + // + + extern uint32_t IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetUsage( inIOHIDDeviceRef ) + // + // Purpose: get the usage for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the usage for this device + // + + extern uint32_t IOHIDDevice_GetUsage(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetUsagePage( inIOHIDDeviceRef ) + // + // Purpose: get the usage page for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the usage page for this device + // + + extern uint32_t IOHIDDevice_GetUsagePage(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetUsagePairs( inIOHIDDeviceRef ) + // + // Purpose: get the UsagePairs CFString for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: CFArrayRef - the UsagePairs for this device + // + + extern CFArrayRef IOHIDDevice_GetUsagePairs(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef ) + // + // Purpose: get the PrimaryUsage CFString for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: CFStringRef - the PrimaryUsage CFString for this device + // + + extern uint32_t IOHIDDevice_GetPrimaryUsage(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef ) + // + // Purpose: get the PrimaryUsagePage CFString for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: CFStringRef - the PrimaryUsagePage CFString for this device + // + + extern uint32_t IOHIDDevice_GetPrimaryUsagePage(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetMaxInputReportSize( inIOHIDDeviceRef ) + // + // Purpose: get the MaxInputReportSize for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the MaxInputReportSize for this device + // + + extern uint32_t IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetMaxOutputReportSize( inIOHIDDeviceRef ) + // + // Purpose: get the MaxOutputReportSize for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the MaxOutputReportSize for this device + // + + extern uint32_t IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetMaxFeatureReportSize( inIOHIDDeviceRef ) + // + // Purpose: get the MaxFeatureReportSize for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the MaxFeatureReportSize for this device + // + + extern uint32_t IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetReportInterval( inIOHIDDeviceRef ) + // + // Purpose: get the ReportInterval for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: uint32_t - the ReportInterval for this device + // + + extern uint32_t IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_GetQueue( inIOHIDDeviceRef ) + // + // Purpose: get the Queue for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: IOHIDQueueRef - the Queue for this device + // + + extern IOHIDQueueRef IOHIDDevice_GetQueue(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_SetQueue( inIOHIDDeviceRef, inQueueRef ) + // + // Purpose: Set the Queue for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // inQueueRef - the Queue + // + // Returns: nothing + // + + extern void IOHIDDevice_SetQueue(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDQueueRef inQueueRef); + + //************************************************************************* + // + // IOHIDDevice_GetTransaction( inIOHIDDeviceRef ) + // + // Purpose: get the Transaction for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // + // Returns: IOHIDTransactionRef - the Transaction for this device + // + + extern IOHIDTransactionRef IOHIDDevice_GetTransaction(IOHIDDeviceRef inIOHIDDeviceRef); + + //************************************************************************* + // + // IOHIDDevice_SetTransaction( inIOHIDDeviceRef, inTransactionRef ) + // + // Purpose: Set the Transaction for this device + // + // Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device + // inTransactionRef - the Transaction + // + // Returns: nothing + // + + extern void IOHIDDevice_SetTransaction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDTransactionRef inTransactionRef); + + //***************************************************** +#if PRAGMA_STRUCT_ALIGN +#pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH +#pragma pack(pop) +#elif PRAGMA_STRUCT_PACK +#pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __IOHIDDevice__ // diff --git a/src/cocoa/IOHIDElement_.c b/src/cocoa/IOHIDElement_.c new file mode 100644 index 000000000..906d2926a --- /dev/null +++ b/src/cocoa/IOHIDElement_.c @@ -0,0 +1,509 @@ +// File: IOHIDElement_.c +// Abstract: convieance functions for IOHIDElementGetProperty +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + +#pragma mark - includes & imports +//----------------------------------------------------- + +#include "IOHIDElement_.h" + +//***************************************************** +#pragma mark - typedef's, struct's, enums, defines, etc. +//----------------------------------------------------- + +//***************************************************** +#pragma mark - local (static) function prototypes +//----------------------------------------------------- + +// static Boolean IOHIDElement_GetLongProperty( IOHIDElementRef inElementRef, CFStringRef inKey, long * outValue ); +// static void IOHIDElement_SetLongProperty( IOHIDElementRef inElementRef, CFStringRef inKey, long inValue ); + +//***************************************************** +#pragma mark - exported globals +//----------------------------------------------------- + +//***************************************************** +#pragma mark - local (static) globals +//----------------------------------------------------- + +//***************************************************** +#pragma mark - exported function implementations +//----------------------------------------------------- + +//************************************************************************* +// +// HIDIsValidElement( inIOHIDElementRef ) +// +// Purpose: validate this element +// +// Inputs: inIOHIDElementRef - the element +// +// Returns: Boolean - TRUE if this is a valid element ref +// +Boolean HIDIsValidElement(IOHIDElementRef inIOHIDElementRef) { + Boolean result = FALSE; // assume failure (pessimist!) + if ( inIOHIDElementRef ) { + if ( CFGetTypeID(inIOHIDElementRef) ==IOHIDElementGetTypeID() ) { + result = TRUE; + } + } + + return (result); +} // HIDIsValidElement + +//************************************************************************* +// +// IOHIDElement_GetValue( inElementRef, inIOHIDValueScaleType ) +// +// Purpose: returns the current value for an element( polling ) +// +// Notes: will return 0 on error conditions which should be accounted for by application +// +// Inputs: inElementRef - the element +// inIOHIDValueScaleType - scale type ( calibrated or physical ) +// +// Returns: double - current value for element +// +double IOHIDElement_GetValue(IOHIDElementRef inElementRef, IOHIDValueScaleType inIOHIDValueScaleType) { + long result = 0; + IOHIDValueRef tIOHIDValueRef; + if ( kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(inElementRef), inElementRef, &tIOHIDValueRef) ) { + result = IOHIDValueGetScaledValue(tIOHIDValueRef, inIOHIDValueScaleType); + } + + return (result); +} // IOHIDElement_GetValue + +//************************************************************************* +// +// IOHIDElement_GetCalibrationMin( inElementRef ) +// +// Purpose: get the minimum bounds for a calibrated value for this element +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// +// Returns: CFIndex - the minimum Calibration value for this element +// + +CFIndex IOHIDElement_GetCalibrationMin(IOHIDElementRef inElementRef) { + CFIndex result; + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMinKey), &result) ) { + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMaxKey), &result) ) { + result = 0x7FFFFFFF; + } + + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMinKey), result); + } + + return (result); +} // IOHIDElement_GetCalibrationMin + +//************************************************************************* +// +// IOHIDElement_SetCalibrationMin( inElementRef, inValue ) +// +// Purpose: set the minimum bounds for a calibrated value for this element +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// inValue - the minimum bounds for a calibrated value for this element +// +// Returns: nothing +// + +void IOHIDElement_SetCalibrationMin(IOHIDElementRef inElementRef, CFIndex inValue) { + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMinKey), inValue); +} // IOHIDElement_SetCalibrationMin + +//************************************************************************* +// +// IOHIDElement_GetCalibrationMax( inElementRef ) +// +// Purpose: get the maximum bounds for a calibrated value for this element +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// +// Returns: CFIndex - the maximum Calibration value for this element +// + +CFIndex IOHIDElement_GetCalibrationMax(IOHIDElementRef inElementRef) { + CFIndex result; + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMaxKey), &result) ) { + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { + result = -0x7FFFFFFF; + } + + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMaxKey), result); + } + + return (result); +} // IOHIDElement_GetCalibrationMax + +//************************************************************************* +// +// IOHIDElement_SetCalibrationMax( inElementRef, inValue ) +// +// Purpose: set the maximum bounds for a calibrated value for this element +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// inValue - the maximum Calibration value for this element +// +// Returns: nothing +// + +void IOHIDElement_SetCalibrationMax(IOHIDElementRef inElementRef, CFIndex inValue) { + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMaxKey), inValue); +} // IOHIDElement_SetCalibrationMax + +//************************************************************************* +// +// IOHIDElement_GetCalibrationSaturationMin( inElementRef ) +// +// Purpose: get the mininum tolerance to be used when calibrating a logical element value +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// +// Returns: CFIndex - the maximum Calibration value for this element +// + +CFIndex IOHIDElement_GetCalibrationSaturationMin(IOHIDElementRef inElementRef) { + CFIndex result; + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMinKey), &result) ) { + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { + result = -0x7FFFFFFF; + } + + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMinKey), result); + } + + return (result); +} // IOHIDElement_GetCalibrationSaturationMin + +//************************************************************************* +// +// IOHIDElement_SetCalibrationSaturationMin( inElementRef, inValue ) +// +// Purpose: set the mininum tolerance to be used when calibrating a logical element value +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// inValue - the maximum Calibration value for this element +// +// Returns: nothing +// + +void IOHIDElement_SetCalibrationSaturationMin(IOHIDElementRef inElementRef, CFIndex inValue) { + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMinKey), inValue); +} // IOHIDElement_SetCalibrationSaturationMin + +//************************************************************************* +// +// IOHIDElement_GetCalibrationSaturationMax( inElementRef ) +// +// Purpose: get the maximum tolerance to be used when calibrating a logical element value +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// +// Returns: CFIndex - the maximum Calibration value for this element +// + +CFIndex IOHIDElement_GetCalibrationSaturationMax(IOHIDElementRef inElementRef) { + CFIndex result; + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMaxKey), &result) ) { + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { + result = -0x7FFFFFFF; + } + + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMaxKey), result); + } + + return (result); +} // IOHIDElement_GetCalibrationSaturationMax + +//************************************************************************* +// +// IOHIDElement_SetCalibrationSaturationMax( inElementRef, inValue ) +// +// Purpose: set the maximum tolerance to be used when calibrating a logical element value +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// inValue - the maximum Calibration value for this element +// +// Returns: nothing +// + +void IOHIDElement_SetCalibrationSaturationMax(IOHIDElementRef inElementRef, CFIndex inValue) { + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMaxKey), inValue); +} // IOHIDElement_SetCalibrationSaturationMax + +//************************************************************************* +// +// IOHIDElement_GetCalibrationDeadZoneMin( inElementRef ) +// +// Purpose: get the minimum bounds near the midpoint of a logical value in which the value is ignored +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// +// Returns: CFIndex - the maximum Calibration value for this element +// + +CFIndex IOHIDElement_GetCalibrationDeadZoneMin(IOHIDElementRef inElementRef) { + CFIndex result; + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey), &result) ) { + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { + result = -0x7FFFFFFF; + } + + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey), result); + } + + return (result); +} // IOHIDElement_GetCalibrationDeadZoneMin + +//************************************************************************* +// +// IOHIDElement_SetCalibrationDeadZoneMin( inElementRef, inValue ) +// +// Purpose: set the minimum bounds near the midpoint of a logical value in which the value is ignored +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// inValue - the maximum Calibration value for this element +// +// Returns: nothing +// + +void IOHIDElement_SetCalibrationDeadZoneMin(IOHIDElementRef inElementRef, CFIndex inValue) { + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey), inValue); +} // IOHIDElement_SetCalibrationDeadZoneMin + +//************************************************************************* +// +// IOHIDElement_GetCalibrationDeadZoneMax( inElementRef ) +// +// Purpose: get the maximum bounds near the midpoint of a logical value in which the value is ignored +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// +// Returns: CFIndex - the maximum Calibration value for this element +// + +CFIndex IOHIDElement_GetCalibrationDeadZoneMax(IOHIDElementRef inElementRef) { + CFIndex result; + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey), &result) ) { + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { + result = -0x7FFFFFFF; + } + + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey), result); + } + + return (result); +} // IOHIDElement_GetCalibrationDeadZoneMax + +//************************************************************************* +// +// IOHIDElement_SetCalibrationDeadZoneMax( inElementRef, inValue ) +// +// Purpose: set the maximum bounds near the midpoint of a logical value in which the value is ignored +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// inValue - the maximum Calibration value for this element +// +// Returns: nothing +// + +void IOHIDElement_SetCalibrationDeadZoneMax(IOHIDElementRef inElementRef, CFIndex inValue) { + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey), inValue); +} // IOHIDElement_SetCalibrationDeadZoneMax + +//************************************************************************* +// +// IOHIDElement_GetCalibrationGranularity( inElementRef ) +// +// Purpose: get the level of detail returned for a calibrated element value +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// +// Returns: double_t - the maximum Calibration value for this element +// + +double_t IOHIDElement_GetCalibrationGranularity(IOHIDElementRef inElementRef) { + CFIndex result; + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationGranularityKey), &result) ) { + if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) { + result = -0x7FFFFFFF; + } + + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationGranularityKey), result); + } + + return (result); +} // IOHIDElement_GetCalibrationGranularity + +//************************************************************************* +// +// IOHIDElement_SetCalibrationGranularity( inElementRef, inValue ) +// +// Purpose: set the level of detail returned for a calibrated element value +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// inValue - the the level of detail for this element +// +// Returns: nothing +// + +void IOHIDElement_SetCalibrationGranularity(IOHIDElementRef inElementRef, double_t inValue) { + IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationGranularityKey), inValue); +} // IOHIDElement_SetCalibrationGranularity + +//************************************************************************* +// +// IOHIDElement_SetupCalibration( inElementRef ) +// +// Purpose: set default values for the element calibration parameters +// +// Inputs: inElementRef - the IOHIDElementRef for this element +// +// Returns: nothing +// +void IOHIDElement_SetupCalibration(IOHIDElementRef inIOHIDElementRef) { + // these are the min/max values returned by IOHIDValueGetScaledValue( v, kIOHIDValueScaleTypeCalibrated ); + IOHIDElement_SetCalibrationMin( inIOHIDElementRef, IOHIDElementGetLogicalMin(inIOHIDElementRef) ); + IOHIDElement_SetCalibrationMax( inIOHIDElementRef, IOHIDElementGetLogicalMax(inIOHIDElementRef) ); + + // this is the granularity of the values returned by IOHIDValueGetScaledValue( v, kIOHIDValueScaleTypeCalibrated ); + // for example if set to 0.1 the values returned will be multiples of 0.1 ( 0.1, 0.2, 0.3, etc. ) + IOHIDElement_SetCalibrationGranularity(inIOHIDElementRef, 0.); + + // these define the dead zone (like in the middel of joystick axis) + IOHIDElement_SetCalibrationDeadZoneMin(inIOHIDElementRef, 0); + IOHIDElement_SetCalibrationDeadZoneMax(inIOHIDElementRef, 0); +#if 1 + // get the current value of this element + double value = IOHIDElement_GetValue(inIOHIDElementRef, kIOHIDValueScaleTypePhysical); + // use it as our min/mas saturation + IOHIDElement_SetCalibrationSaturationMin(inIOHIDElementRef, value); + IOHIDElement_SetCalibrationSaturationMax(inIOHIDElementRef, value); +#else + // calculate the middle physical value we would expect from this element + CFIndex valueMin = IOHIDElementGetPhysicalMin(inIOHIDElementRef); + CFIndex valueMax = IOHIDElementGetPhysicalMax(inIOHIDElementRef); + CFIndex valueMid = (valueMin + valueMax) / 2; + + // use it as our min/mas saturation + // this value determines the min/max values that have been recieved from the device element + IOHIDElement_SetCalibrationSaturationMin(inIOHIDElementRef, valueMid); + IOHIDElement_SetCalibrationSaturationMax(inIOHIDElementRef, valueMid); + + // get the current value of this element + double value = IOHIDElement_GetValue(inIOHIDElementRef, kIOHIDValueScaleTypePhysical); + // and use it to adjust the current saturation values if it's outside their range + if ( value < IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef) ) { + IOHIDElement_SetCalibrationSaturationMin(inIOHIDElementRef, value); + } + if ( value > IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef) ) { + IOHIDElement_SetCalibrationSaturationMax(inIOHIDElementRef, value); + } + +#endif +} // IOHIDElement_SetupCalibration +//***************************************************** +#pragma mark - local (static) function implementations +//----------------------------------------------------- + +//************************************************************************* +// +// IOHIDElement_GetLongProperty( inElementRef, inKey, outValue ) +// +// Purpose: convieance function to return a long property of an element +// +// Inputs: inElementRef - the element +// inKey - CFString for the key +// outValue - address where to store the value +// Returns: Boolean - TRUE if successful +// outValue - the long property's value +// + +Boolean IOHIDElement_GetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long *outValue) { + Boolean result = FALSE; + + CFTypeRef tCFTypeRef = IOHIDElementGetProperty(inElementRef, inKey); + if ( tCFTypeRef ) { + // if this is a number + if ( CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef) ) { + // get it's value + result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue ); + } + } + + return (result); +} /* IOHIDElement_GetLongProperty */ + +//************************************************************************* +// +// IOHIDElement_SetLongProperty( inElementRef, inKey, inValue ) +// +// Purpose: convieance function to set a long property of an element +// +// Inputs: inElementRef - the element +// inKey - CFString for the key +// inValue - the value to set it to +// +// Returns: nothing +// + +void IOHIDElement_SetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long inValue) { + CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue); + if ( tCFNumberRef ) { + IOHIDElementSetProperty(inElementRef, inKey, tCFNumberRef); + CFRelease(tCFNumberRef); + } +} // IOHIDElement_SetLongProperty + +//***************************************************** + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/IOHIDElement_.h b/src/cocoa/IOHIDElement_.h new file mode 100644 index 000000000..a8a631668 --- /dev/null +++ b/src/cocoa/IOHIDElement_.h @@ -0,0 +1,339 @@ +// File: IOHIDElement_.h +// Abstract: convieance functions for IOHIDElementGetProperty +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** +#ifndef __IOHIDElement___ +#define __IOHIDElement___ + +//***************************************************** +#pragma mark - includes & imports + +#include + +#include "IOHIDLib_.h" +//***************************************************** +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN +#pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH +#pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK +#pragma pack(2) +#endif + + //***************************************************** +#pragma mark - typedef's, struct's, enums, defines, etc. + //----------------------------------------------------- + + //***************************************************** +#pragma mark - exported globals + //----------------------------------------------------- + + //***************************************************** +#pragma mark - exported function prototypes + //----------------------------------------------------- + + //************************************************************************* + // + // HIDIsValidElement( inIOHIDElementRef ) + // + // Purpose: validate this element + // + // Inputs: inIOHIDElementRef - the element + // + // Returns: Boolean - TRUE if this is a valid element ref + // + extern Boolean HIDIsValidElement(IOHIDElementRef inIOHIDElementRef); + + //************************************************************************* + // + // IOHIDElement_GetValue( inElementRef, inIOHIDValueScaleType ) + // + // Purpose: returns the current value for an element( polling ) + // + // Notes: will return 0 on error conditions which should be accounted for by application + // + // Inputs: inElementRef - the element + // inIOHIDValueScaleType - scale type ( calibrated or physical ) + // + // Returns: double - current value for element + // + extern double IOHIDElement_GetValue(IOHIDElementRef inElementRef, IOHIDValueScaleType inIOHIDValueScaleType); + + //************************************************************************* + // + // IOHIDElement_GetCalibrationMin( inElementRef ) + // + // Purpose: get the minimum bounds for a calibrated value for this element + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // + // Returns: CFIndex - the minimum Calibration value for this element + // + + extern CFIndex IOHIDElement_GetCalibrationMin(IOHIDElementRef inElementRef); + + //************************************************************************* + // + // IOHIDElement_SetCalibrationMin( inElementRef, inValue ) + // + // Purpose: set the minimum bounds for a calibrated value for this element + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // inValue - the minimum bounds for a calibrated value for this element + // + // Returns: nothing + // + + extern void IOHIDElement_SetCalibrationMin(IOHIDElementRef inElementRef, CFIndex inValue); + + //************************************************************************* + // + // IOHIDElement_GetCalibrationMax( inElementRef ) + // + // Purpose: get the maximum bounds for a calibrated value for this element + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // + // Returns: CFIndex - the maximum Calibration value for this element + // + + extern CFIndex IOHIDElement_GetCalibrationMax(IOHIDElementRef inElementRef); + + //************************************************************************* + // + // IOHIDElement_SetCalibrationMax( inElementRef, inValue ) + // + // Purpose: set the maximum bounds for a calibrated value for this element + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // inValue - the maximum Calibration value for this element + // + // Returns: nothing + // + + extern void IOHIDElement_SetCalibrationMax(IOHIDElementRef inElementRef, CFIndex inValue); + + //************************************************************************* + // + // IOHIDElement_GetCalibrationSaturationMin( inElementRef ) + // + // Purpose: get the mininum tolerance to be used when calibrating a logical element value + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // + // Returns: CFIndex - the maximum Calibration value for this element + // + + extern CFIndex IOHIDElement_GetCalibrationSaturationMin(IOHIDElementRef inElementRef); + + //************************************************************************* + // + // IOHIDElement_SetCalibrationSaturationMin( inElementRef, inValue ) + // + // Purpose: set the mininum tolerance to be used when calibrating a logical element value + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // inValue - the maximum Calibration value for this element + // + // Returns: nothing + // + + extern void IOHIDElement_SetCalibrationSaturationMin(IOHIDElementRef inElementRef, CFIndex inValue); + + //************************************************************************* + // + // IOHIDElement_GetCalibrationSaturationMax( inElementRef ) + // + // Purpose: get the maximum tolerance to be used when calibrating a logical element value + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // + // Returns: CFIndex - the maximum Calibration value for this element + // + + extern CFIndex IOHIDElement_GetCalibrationSaturationMax(IOHIDElementRef inElementRef); + + //************************************************************************* + // + // IOHIDElement_SetCalibrationSaturationMax( inElementRef, inValue ) + // + // Purpose: set the maximum tolerance to be used when calibrating a logical element value + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // inValue - the maximum Calibration value for this element + // + // Returns: nothing + // + + extern void IOHIDElement_SetCalibrationSaturationMax(IOHIDElementRef inElementRef, CFIndex inValue); + + //************************************************************************* + // + // IOHIDElement_GetCalibrationDeadZoneMin( inElementRef ) + // + // Purpose: get the minimum bounds near the midpoint of a logical value in which the value is ignored + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // + // Returns: CFIndex - the maximum Calibration value for this element + // + + extern CFIndex IOHIDElement_GetCalibrationDeadZoneMin(IOHIDElementRef inElementRef); + + //************************************************************************* + // + // IOHIDElement_SetCalibrationDeadZoneMin( inElementRef, inValue ) + // + // Purpose: set the minimum bounds near the midpoint of a logical value in which the value is ignored + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // inValue - the maximum Calibration value for this element + // + // Returns: nothing + // + + extern void IOHIDElement_SetCalibrationDeadZoneMin(IOHIDElementRef inElementRef, CFIndex inValue); + + //************************************************************************* + // + // IOHIDElement_GetCalibrationDeadZoneMax( inElementRef ) + // + // Purpose: get the maximum bounds near the midpoint of a logical value in which the value is ignored + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // + // Returns: CFIndex - the maximum Calibration value for this element + // + + extern CFIndex IOHIDElement_GetCalibrationDeadZoneMax(IOHIDElementRef inElementRef); + + //************************************************************************* + // + // IOHIDElement_SetCalibrationDeadZoneMax( inElementRef, inValue ) + // + // Purpose: set the maximum bounds near the midpoint of a logical value in which the value is ignored + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // inValue - the maximum Calibration value for this element + // + // Returns: nothing + // + + extern void IOHIDElement_SetCalibrationDeadZoneMax(IOHIDElementRef inElementRef, CFIndex inValue); + + //************************************************************************* + // + // IOHIDElement_GetCalibrationGranularity( inElementRef ) + // + // Purpose: get the level of detail returned for a calibrated element value + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // + // Returns: double_t - the maximum Calibration value for this element + // + + extern double_t IOHIDElement_GetCalibrationGranularity(IOHIDElementRef inElementRef); + + //************************************************************************* + // + // IOHIDElement_SetCalibrationGranularity( inElementRef, inValue ) + // + // Purpose: set the level of detail returned for a calibrated element value + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // inValue - the the level of detail for this element + // + // Returns: nothing + // + + extern void IOHIDElement_SetCalibrationGranularity(IOHIDElementRef inElementRef, double_t inValue); + + //************************************************************************* + // + // IOHIDElement_SetupCalibration( inElementRef ) + // + // Purpose: set default values for the element calibration parameters + // + // Inputs: inElementRef - the IOHIDElementRef for this element + // + // Returns: nothing + // + + extern void IOHIDElement_SetupCalibration(IOHIDElementRef inIOHIDElementRef); + + extern Boolean IOHIDElement_GetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long *outValue); + extern void IOHIDElement_SetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long inValue); + + //***************************************************** +#if PRAGMA_STRUCT_ALIGN +#pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH +#pragma pack(pop) +#elif PRAGMA_STRUCT_PACK +#pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __IOHIDElement___ // diff --git a/src/cocoa/IOHIDLib_.h b/src/cocoa/IOHIDLib_.h new file mode 100644 index 000000000..38c6248ac --- /dev/null +++ b/src/cocoa/IOHIDLib_.h @@ -0,0 +1,111 @@ +// File: IOHIDLib_.h +// Abstract: Single include file for all header files of IOHIDLib +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** +#ifndef __IOHIDLib___ +#define __IOHIDLib___ + +//***************************************************** +#pragma mark - includes & imports +//----------------------------------------------------- +#include + +#include "IOHIDDevice_.h" +#include "IOHIDElement_.h" + +#include "ImmrHIDUtilAddOn.h" + +//***************************************************** +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN +#pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH +#pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK +#pragma pack(2) +#endif + + //***************************************************** +#pragma mark - typedef's, struct's, enums, defines, etc. + //----------------------------------------------------- + + //***************************************************** +#pragma mark - exported globals + //----------------------------------------------------- + + //***************************************************** +#pragma mark - exported function prototypes + //----------------------------------------------------- + + //***************************************************** +#if PRAGMA_STRUCT_ALIGN +#pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH +#pragma pack(pop) +#elif PRAGMA_STRUCT_PACK +#pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __IOHIDLib___ diff --git a/src/cocoa/ImmrHIDUtilAddOn.c b/src/cocoa/ImmrHIDUtilAddOn.c new file mode 100644 index 000000000..4937d3687 --- /dev/null +++ b/src/cocoa/ImmrHIDUtilAddOn.c @@ -0,0 +1,108 @@ +// File: ImmrHIDUtilAddOn.c +// Abstract: Glue code to convert IOHIDDeviceRef's to (FFB) io_object_t's +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + +#include +#include + +#include "ImmrHIDUtilAddOn.h" + +//--------------------------------------------------------------------------------- +// +// AllocateHIDObjectFromIOHIDDeviceRef( ) +// +// returns: +// NULL, or acceptable io_object_t +// +//--------------------------------------------------------------------------------- +io_service_t AllocateHIDObjectFromIOHIDDeviceRef(IOHIDDeviceRef inIOHIDDeviceRef) { + io_service_t result = 0L; + if ( inIOHIDDeviceRef ) { + // Set up the matching criteria for the devices we're interested in. + // We are interested in instances of class IOHIDDevice. + // matchingDict is consumed below( in IOServiceGetMatchingService ) + // so we have no leak here. + CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOHIDDeviceKey); + if ( matchingDict ) { + // Add a key for locationID to our matching dictionary. This works for matching to + // IOHIDDevices, so we will only look for a device attached to that particular port + // on the machine. + CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey) ); + if ( tCFTypeRef ) { + CFDictionaryAddValue(matchingDict, CFSTR(kIOHIDLocationIDKey), tCFTypeRef); + // CFRelease( tCFTypeRef ); // don't release objects that we "Get". + + // IOServiceGetMatchingService assumes that we already know that there is only one device + // that matches. This way we don't have to do the whole iteration dance to look at each + // device that matches. This is a new API in 10.2 + result = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict); + } + + // Note: We're not leaking the matchingDict. + // One reference is consumed by IOServiceGetMatchingServices + } + } + + return (result); +} // AllocateHIDObjectFromIOHIDDeviceRef + +//--------------------------------------------------------------------------------- +// +// FreeHIDObject( ) +// +//--------------------------------------------------------------------------------- +bool FreeHIDObject(io_service_t inHIDObject) { + kern_return_t kr; + + kr = IOObjectRelease(inHIDObject); + + return (kIOReturnSuccess == kr); +} // FreeHIDObject + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 diff --git a/src/cocoa/ImmrHIDUtilAddOn.h b/src/cocoa/ImmrHIDUtilAddOn.h new file mode 100644 index 000000000..72de752e3 --- /dev/null +++ b/src/cocoa/ImmrHIDUtilAddOn.h @@ -0,0 +1,50 @@ +// File: ImmrHIDUtilAddOn.h +// Abstract: Glue code to convert IOHIDDeviceRef's to (FFB) io_object_t's +// Version: 2.0 +// +// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple +// Inc. ("Apple") in consideration of your agreement to the following +// terms, and your use, installation, modification or redistribution of +// this Apple software constitutes acceptance of these terms. If you do +// not agree with these terms, please do not use, install, modify or +// redistribute this Apple software. +// +// In consideration of your agreement to abide by the following terms, and +// subject to these terms, Apple grants you a personal, non-exclusive +// license, under Apple's copyrights in this original Apple software (the +// "Apple Software"), to use, reproduce, modify and redistribute the Apple +// Software, with or without modifications, in source and/or binary forms; +// provided that if you redistribute the Apple Software in its entirety and +// without modifications, you must retain this notice and the following +// text and disclaimers in all such redistributions of the Apple Software. +// Neither the name, trademarks, service marks or logos of Apple Inc. may +// be used to endorse or promote products derived from the Apple Software +// without specific prior written permission from Apple. Except as +// expressly stated in this notice, no other rights or licenses, express or +// implied, are granted by Apple herein, including but not limited to any +// patent rights that may be infringed by your derivative works or by other +// works in which the Apple Software may be incorporated. +// +// The Apple Software is provided by Apple on an "AS IS" basis. APPLE +// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. +// +// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (C) 2009 Apple Inc. All Rights Reserved. +// +//***************************************************** +#include +#include + +extern io_service_t AllocateHIDObjectFromIOHIDDeviceRef(IOHIDDeviceRef inIOHIDDeviceRef); +extern bool FreeHIDObject(io_object_t inHIDObject); diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm new file mode 100644 index 000000000..bd3dc3115 --- /dev/null +++ b/src/cocoa/i_backend_cocoa.mm @@ -0,0 +1,2171 @@ +/* + ** i_backend_cocoa.mm + ** + **--------------------------------------------------------------------------- + ** Copyright 2012-2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +// Avoid collision between DObject class and Objective-C +#define Class ObjectClass + +#include "bitmap.h" +#include "c_console.h" +#include "c_dispatch.h" +#include "cmdlib.h" +#include "d_event.h" +#include "d_gui.h" +#include "dikeys.h" +#include "doomdef.h" +#include "doomstat.h" +#include "s_sound.h" +#include "textures.h" +#include "v_video.h" +#include "version.h" +#include "i_rbopts.h" +#include "i_osversion.h" + +#undef Class + + +#define ZD_UNUSED(VARIABLE) ((void)(VARIABLE)) + + +// --------------------------------------------------------------------------- + + +// The following definitions are required to build with older OS X SDKs + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 + +typedef unsigned int NSUInteger; +typedef int NSInteger; + +typedef float CGFloat; + +// From HIToolbox/Events.h +enum +{ + kVK_Return = 0x24, + kVK_Tab = 0x30, + kVK_Space = 0x31, + kVK_Delete = 0x33, + kVK_Escape = 0x35, + kVK_Command = 0x37, + kVK_Shift = 0x38, + kVK_CapsLock = 0x39, + kVK_Option = 0x3A, + kVK_Control = 0x3B, + kVK_RightShift = 0x3C, + kVK_RightOption = 0x3D, + kVK_RightControl = 0x3E, + kVK_Function = 0x3F, + kVK_F17 = 0x40, + kVK_VolumeUp = 0x48, + kVK_VolumeDown = 0x49, + kVK_Mute = 0x4A, + kVK_F18 = 0x4F, + kVK_F19 = 0x50, + kVK_F20 = 0x5A, + kVK_F5 = 0x60, + kVK_F6 = 0x61, + kVK_F7 = 0x62, + kVK_F3 = 0x63, + kVK_F8 = 0x64, + kVK_F9 = 0x65, + kVK_F11 = 0x67, + kVK_F13 = 0x69, + kVK_F16 = 0x6A, + kVK_F14 = 0x6B, + kVK_F10 = 0x6D, + kVK_F12 = 0x6F, + kVK_F15 = 0x71, + kVK_Help = 0x72, + kVK_Home = 0x73, + kVK_PageUp = 0x74, + kVK_ForwardDelete = 0x75, + kVK_F4 = 0x76, + kVK_End = 0x77, + kVK_F2 = 0x78, + kVK_PageDown = 0x79, + kVK_F1 = 0x7A, + kVK_LeftArrow = 0x7B, + kVK_RightArrow = 0x7C, + kVK_DownArrow = 0x7D, + kVK_UpArrow = 0x7E +}; + +@interface NSView(SupportOutdatedOSX) +- (NSPoint)convertPointFromBase:(NSPoint)aPoint; +@end + +@implementation NSView(SupportOutdatedOSX) +- (NSPoint)convertPointFromBase:(NSPoint)aPoint +{ + return [self convertPoint:aPoint fromView:nil]; +} +@end + +#endif // prior to 10.5 + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 + +enum +{ + NSApplicationActivationPolicyRegular +}; + +typedef NSInteger NSApplicationActivationPolicy; + +@interface NSApplication(ActivationPolicy) +- (BOOL)setActivationPolicy:(NSApplicationActivationPolicy)activationPolicy; +@end + +@interface NSWindow(SetStyleMask) +- (void)setStyleMask:(NSUInteger)styleMask; +@end + +#endif // prior to 10.6 + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 + +@interface NSView(HiDPIStubs) +- (NSPoint)convertPointToBacking:(NSPoint)aPoint; +- (NSSize)convertSizeToBacking:(NSSize)aSize; +- (NSSize)convertSizeFromBacking:(NSSize)aSize; + +- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag; +@end + +@interface NSScreen(HiDPIStubs) +- (NSRect)convertRectToBacking:(NSRect)aRect; +@end + +#endif // prior to 10.7 + + +// --------------------------------------------------------------------------- + + +RenderBufferOptions rbOpts; + +EXTERN_CVAR(Bool, fullscreen) +EXTERN_CVAR(Bool, vid_hidpi) + +CVAR(Bool, use_mouse, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, m_filter, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) +{ + if (self < 0) + { + self = 0; + } + else if (self > 2) + { + self = 2; + } +} + +bool GUICapture; + + +extern int paused, chatmodeon; +extern constate_e ConsoleState; + +EXTERN_CVAR(Int, m_use_mouse); + +void I_ShutdownJoysticks(); + + +namespace +{ + +const int ARGC_MAX = 64; + +int s_argc; +char* s_argv[ARGC_MAX]; + +TArray s_argvStorage; + +bool s_restartedFromWADPicker; + + +bool s_nativeMouse = true; + +// TODO: remove this magic! +size_t s_skipMouseMoves; + +NSCursor* s_cursor; + + +void CheckGUICapture() +{ + const bool wantCapture = (MENU_Off == menuactive) + ? (c_down == ConsoleState || c_falling == ConsoleState || chatmodeon) + : (menuactive == MENU_On || menuactive == MENU_OnNoPause); + + if (wantCapture != GUICapture) + { + GUICapture = wantCapture; + + ResetButtonStates(); + } +} + +void CenterCursor() +{ + NSWindow* window = [NSApp keyWindow]; + if (nil == window) + { + return; + } + + const NSRect displayRect = [[window screen] frame]; + const NSRect windowRect = [window frame]; + const CGPoint centerPoint = CGPointMake(NSMidX(windowRect), displayRect.size.height - NSMidY(windowRect)); + + CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); + + if (NULL != eventSource) + { + CGEventRef mouseMoveEvent = CGEventCreateMouseEvent(eventSource, + kCGEventMouseMoved, centerPoint, kCGMouseButtonLeft); + + if (NULL != mouseMoveEvent) + { + CGEventPost(kCGHIDEventTap, mouseMoveEvent); + CFRelease(mouseMoveEvent); + } + + CFRelease(eventSource); + } + + // TODO: remove this magic! + s_skipMouseMoves = 2; +} + + +bool IsInGame() +{ + switch (mouse_capturemode) + { + default: + case 0: + return gamestate == GS_LEVEL; + + case 1: + return gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_FINALE; + + case 2: + return true; + } +} + +void SetNativeMouse(bool wantNative) +{ + if (wantNative != s_nativeMouse) + { + s_nativeMouse = wantNative; + + if (!wantNative) + { + CenterCursor(); + } + + CGAssociateMouseAndMouseCursorPosition(wantNative); + + if (wantNative) + { + [NSCursor unhide]; + } + else + { + [NSCursor hide]; + } + } +} + +void CheckNativeMouse() +{ + bool windowed = (NULL == screen) || !screen->IsFullscreen(); + bool wantNative; + + if (windowed) + { + if (![NSApp isActive] || !use_mouse) + { + wantNative = true; + } + else if (MENU_WaitKey == menuactive) + { + wantNative = false; + } + else + { + wantNative = (!m_use_mouse || MENU_WaitKey != menuactive) + && (!IsInGame() || GUICapture || paused || demoplayback); + } + } + else + { + // ungrab mouse when in the menu with mouse control on. + wantNative = m_use_mouse + && (MENU_On == menuactive || MENU_OnNoPause == menuactive); + } + + SetNativeMouse(wantNative); +} + +} // unnamed namespace + + +// see cocoa/i_joystick.cpp +void I_ProcessJoysticks(); + + +void I_GetEvent() +{ + [[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode]; +} + +void I_StartTic() +{ + CheckGUICapture(); + CheckNativeMouse(); + + I_ProcessJoysticks(); + I_GetEvent(); +} + +void I_StartFrame() +{ + +} + + +void I_SetMouseCapture() +{ + +} + +void I_ReleaseMouseCapture() +{ + +} + + +// --------------------------------------------------------------------------- + + +namespace +{ + +const size_t KEY_COUNT = 128; + + +// See Carbon -> HIToolbox -> Events.h for kVK_ constants + +const uint8_t KEYCODE_TO_DIK[KEY_COUNT] = +{ + DIK_A, DIK_S, DIK_D, DIK_F, DIK_H, DIK_G, DIK_Z, DIK_X, // 0x00 - 0x07 + DIK_C, DIK_V, 0, DIK_B, DIK_Q, DIK_W, DIK_E, DIK_R, // 0x08 - 0x0F + DIK_Y, DIK_T, DIK_1, DIK_2, DIK_3, DIK_4, DIK_6, DIK_5, // 0x10 - 0x17 + DIK_EQUALS, DIK_9, DIK_7, DIK_MINUS, DIK_8, DIK_0, DIK_RBRACKET, DIK_O, // 0x18 - 0x1F + DIK_U, DIK_LBRACKET, DIK_I, DIK_P, DIK_RETURN, DIK_L, DIK_J, DIK_APOSTROPHE, // 0x20 - 0x27 + DIK_K, DIK_SEMICOLON, DIK_BACKSLASH, DIK_COMMA, DIK_SLASH, DIK_N, DIK_M, DIK_PERIOD, // 0x28 - 0x2F + DIK_TAB, DIK_SPACE, DIK_GRAVE, DIK_BACK, 0, DIK_ESCAPE, 0, DIK_LWIN, // 0x30 - 0x37 + DIK_LSHIFT, DIK_CAPITAL, DIK_LMENU, DIK_LCONTROL, DIK_RSHIFT, DIK_RMENU, DIK_RCONTROL, 0, // 0x38 - 0x3F + 0, DIK_DECIMAL, 0, DIK_MULTIPLY, 0, DIK_ADD, 0, 0, // 0x40 - 0x47 + DIK_VOLUMEUP, DIK_VOLUMEDOWN, DIK_MUTE, DIK_SLASH, DIK_NUMPADENTER, 0, DIK_SUBTRACT, 0, // 0x48 - 0x4F + 0, DIK_NUMPAD_EQUALS, DIK_NUMPAD0, DIK_NUMPAD1, DIK_NUMPAD2, DIK_NUMPAD3, DIK_NUMPAD4, DIK_NUMPAD5, // 0x50 - 0x57 + DIK_NUMPAD6, DIK_NUMPAD7, 0, DIK_NUMPAD8, DIK_NUMPAD9, 0, 0, 0, // 0x58 - 0x5F + DIK_F5, DIK_F6, DIK_F7, DIK_F3, DIK_F8, DIK_F9, 0, DIK_F11, // 0x60 - 0x67 + 0, DIK_F13, 0, DIK_F14, 0, DIK_F10, 0, DIK_F12, // 0x68 - 0x6F + 0, DIK_F15, 0, DIK_HOME, 0, DIK_DELETE, DIK_F4, DIK_END, // 0x70 - 0x77 + DIK_F2, 0, DIK_F1, DIK_LEFT, DIK_RIGHT, DIK_DOWN, DIK_UP, 0, // 0x78 - 0x7F +}; + +const uint8_t KEYCODE_TO_ASCII[KEY_COUNT] = +{ + 'a', 's', 'd', 'f', 'h', 'g', 'z', 'x', // 0x00 - 0x07 + 'c', 'v', 0, 'b', 'q', 'w', 'e', 'r', // 0x08 - 0x0F + 'y', 't', '1', '2', '3', '4', '6', '5', // 0x10 - 0x17 + '=', '9', '7', '-', '8', '0', ']', 'o', // 0x18 - 0x1F + 'u', '[', 'i', 'p', 13, 'l', 'j', '\'', // 0x20 - 0x27 + 'k', ';', '\\', ',', '/', 'n', 'm', '.', // 0x28 - 0x2F + 9, ' ', '`', 12, 0, 27, 0, 0, // 0x30 - 0x37 + 0, 0, 0, 0, 0, 0, 0, 0, // 0x38 - 0x3F + 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0x47 + 0, 0, 0, 0, 0, 0, 0, 0, // 0x48 - 0x4F + 0, 0, 0, 0, 0, 0, 0, 0, // 0x50 - 0x57 + 0, 0, 0, 0, 0, 0, 0, 0, // 0x58 - 0x5F + 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0x67 + 0, 0, 0, 0, 0, 0, 0, 0, // 0x68 - 0x6F + 0, 0, 0, 0, 0, 0, 0, 0, // 0x70 - 0x77 + 0, 0, 0, 0, 0, 0, 0, 0, // 0x78 - 0x7F +}; + + +uint8_t ModifierToDIK(const uint32_t modifier) +{ + switch (modifier) + { + case NSAlphaShiftKeyMask: return DIK_CAPITAL; + case NSShiftKeyMask: return DIK_LSHIFT; + case NSControlKeyMask: return DIK_LCONTROL; + case NSAlternateKeyMask: return DIK_LMENU; + case NSCommandKeyMask: return DIK_LWIN; + } + + return 0; +} + +SWORD ModifierFlagsToGUIKeyModifiers(NSEvent* theEvent) +{ + const NSUInteger modifiers([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask); + return ((modifiers & NSShiftKeyMask ) ? GKM_SHIFT : 0) + | ((modifiers & NSControlKeyMask ) ? GKM_CTRL : 0) + | ((modifiers & NSAlternateKeyMask) ? GKM_ALT : 0) + | ((modifiers & NSCommandKeyMask ) ? GKM_META : 0); +} + +bool ShouldGenerateGUICharEvent(NSEvent* theEvent) +{ + const NSUInteger modifiers([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask); + return !(modifiers & NSControlKeyMask) + && !(modifiers & NSAlternateKeyMask) + && !(modifiers & NSCommandKeyMask) + && !(modifiers & NSFunctionKeyMask); +} + +void ProcessKeyboardFlagsEvent(NSEvent* theEvent) +{ + static const uint32_t FLAGS_MASK = + NSDeviceIndependentModifierFlagsMask & ~NSNumericPadKeyMask; + + const uint32_t modifiers = [theEvent modifierFlags] & FLAGS_MASK; + static uint32_t oldModifiers = 0; + const uint32_t deltaModifiers = modifiers ^ oldModifiers; + + if (0 == deltaModifiers) + { + return; + } + + event_t event = {}; + + event.type = modifiers > oldModifiers ? EV_KeyDown : EV_KeyUp; + event.data1 = ModifierToDIK(deltaModifiers); + + oldModifiers = modifiers; + + // Caps Lock is a modifier key which generates one event per state change + // but not per actual key press or release. So treat any event as key down + // Also its event should be not be posted in menu and console + + if (DIK_CAPITAL == event.data1) + { + if (GUICapture) + { + return; + } + + event.type = EV_KeyDown; + } + + D_PostEvent(&event); +} + +NSStringEncoding GetEncodingForUnicodeCharacter(const unichar character) +{ + if (character >= L'\u0100' && character <= L'\u024F') + { + return NSWindowsCP1250StringEncoding; // Central and Eastern Europe + } + else if (character >= L'\u0370' && character <= L'\u03FF') + { + return NSWindowsCP1253StringEncoding; // Greek + } + else if (character >= L'\u0400' && character <= L'\u04FF') + { + return NSWindowsCP1251StringEncoding; // Cyrillic + } + + // TODO: add handling for other characters + // TODO: Turkish should use NSWindowsCP1254StringEncoding + + return NSWindowsCP1252StringEncoding; +} + +unsigned char GetCharacterFromNSEvent(NSEvent* theEvent) +{ + const NSString* unicodeCharacters = [theEvent characters]; + + if (0 == [unicodeCharacters length]) + { + return '\0'; + } + + const unichar unicodeCharacter = [unicodeCharacters characterAtIndex:0]; + const NSStringEncoding encoding = GetEncodingForUnicodeCharacter(unicodeCharacter); + + unsigned char character = '\0'; + + if (NSWindowsCP1252StringEncoding == encoding) + { + // TODO: make sure that the following is always correct + character = unicodeCharacter & 0xFF; + } + else + { + const NSData* const characters = + [[theEvent characters] dataUsingEncoding:encoding]; + + character = [characters length] > 0 + ? *static_cast([characters bytes]) + : '\0'; + } + + return character; +} + +void ProcessKeyboardEventInMenu(NSEvent* theEvent) +{ + event_t event = {}; + + event.type = EV_GUI_Event; + event.subtype = NSKeyDown == [theEvent type] ? EV_GUI_KeyDown : EV_GUI_KeyUp; + event.data2 = GetCharacterFromNSEvent(theEvent); + event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent); + + if (EV_GUI_KeyDown == event.subtype && [theEvent isARepeat]) + { + event.subtype = EV_GUI_KeyRepeat; + } + + const unsigned short keyCode = [theEvent keyCode]; + + switch (keyCode) + { + case kVK_Return: event.data1 = GK_RETURN; break; + case kVK_PageUp: event.data1 = GK_PGUP; break; + case kVK_PageDown: event.data1 = GK_PGDN; break; + case kVK_End: event.data1 = GK_END; break; + case kVK_Home: event.data1 = GK_HOME; break; + case kVK_LeftArrow: event.data1 = GK_LEFT; break; + case kVK_RightArrow: event.data1 = GK_RIGHT; break; + case kVK_UpArrow: event.data1 = GK_UP; break; + case kVK_DownArrow: event.data1 = GK_DOWN; break; + case kVK_Delete: event.data1 = GK_BACKSPACE; break; + case kVK_ForwardDelete: event.data1 = GK_DEL; break; + case kVK_Escape: event.data1 = GK_ESCAPE; break; + case kVK_F1: event.data1 = GK_F1; break; + case kVK_F2: event.data1 = GK_F2; break; + case kVK_F3: event.data1 = GK_F3; break; + case kVK_F4: event.data1 = GK_F4; break; + case kVK_F5: event.data1 = GK_F5; break; + case kVK_F6: event.data1 = GK_F6; break; + case kVK_F7: event.data1 = GK_F7; break; + case kVK_F8: event.data1 = GK_F8; break; + case kVK_F9: event.data1 = GK_F9; break; + case kVK_F10: event.data1 = GK_F10; break; + case kVK_F11: event.data1 = GK_F11; break; + case kVK_F12: event.data1 = GK_F12; break; + default: + event.data1 = KEYCODE_TO_ASCII[keyCode]; + break; + } + + if (event.data1 < 128) + { + event.data1 = toupper(event.data1); + + D_PostEvent(&event); + } + + if (!iscntrl(event.data2) + && EV_GUI_KeyUp != event.subtype + && ShouldGenerateGUICharEvent(theEvent)) + { + event.subtype = EV_GUI_Char; + event.data1 = event.data2; + event.data2 = event.data3 & GKM_ALT; + + D_PostEvent(&event); + } +} + +void ProcessKeyboardEvent(NSEvent* theEvent) +{ + const unsigned short keyCode = [theEvent keyCode]; + if (keyCode >= KEY_COUNT) + { + assert(!"Unknown keycode"); + return; + } + + if (GUICapture) + { + ProcessKeyboardEventInMenu(theEvent); + } + else + { + event_t event = {}; + + event.type = NSKeyDown == [theEvent type] ? EV_KeyDown : EV_KeyUp; + event.data1 = KEYCODE_TO_DIK[ keyCode ]; + + if (0 != event.data1) + { + event.data2 = KEYCODE_TO_ASCII[ keyCode ]; + + D_PostEvent(&event); + } + } +} + + +bool IsHiDPISupported() +{ + // The following value shoud be equal to NSAppKitVersionNumber10_7 + // and it's hard-coded in order to build on earlier SDKs + return NSAppKitVersionNumber >= 1138; +} + +NSSize GetRealContentViewSize(const NSWindow* const window) +{ + const NSView* view = [window contentView]; + const NSSize frameSize = [view frame].size; + + // TODO: figure out why [NSView frame] returns different values in "fullscreen" and in window + // In "fullscreen" the result is multiplied by [NSScreen backingScaleFactor], but not in window + + return (vid_hidpi && !fullscreen) + ? [view convertSizeToBacking:frameSize] + : frameSize; +} + + +void NSEventToGameMousePosition(NSEvent* inEvent, event_t* outEvent) +{ + const NSWindow* window = [inEvent window]; + const NSView* view = [window contentView]; + + const NSPoint screenPos = [NSEvent mouseLocation]; + const NSPoint windowPos = [window convertScreenToBase:screenPos]; + + const NSPoint viewPos = IsHiDPISupported() + ? [view convertPointToBacking:windowPos] + : [view convertPoint:windowPos fromView:nil]; + + const CGFloat frameHeight = GetRealContentViewSize(window).height; + + const CGFloat posX = ( viewPos.x - rbOpts.shiftX) / rbOpts.pixelScale; + const CGFloat posY = (frameHeight - viewPos.y - rbOpts.shiftY) / rbOpts.pixelScale; + + outEvent->data1 = static_cast< int >(posX); + outEvent->data2 = static_cast< int >(posY); +} + +void ProcessMouseButtonEvent(NSEvent* theEvent) +{ + event_t event = {}; + + const NSEventType cocoaEventType = [theEvent type]; + + if (GUICapture) + { + event.type = EV_GUI_Event; + + switch (cocoaEventType) + { + case NSLeftMouseDown: event.subtype = EV_GUI_LButtonDown; break; + case NSRightMouseDown: event.subtype = EV_GUI_RButtonDown; break; + case NSOtherMouseDown: event.subtype = EV_GUI_MButtonDown; break; + case NSLeftMouseUp: event.subtype = EV_GUI_LButtonUp; break; + case NSRightMouseUp: event.subtype = EV_GUI_RButtonUp; break; + case NSOtherMouseUp: event.subtype = EV_GUI_MButtonUp; break; + default: break; + } + + NSEventToGameMousePosition(theEvent, &event); + + D_PostEvent(&event); + } + else + { + switch (cocoaEventType) + { + case NSLeftMouseDown: + case NSRightMouseDown: + case NSOtherMouseDown: + event.type = EV_KeyDown; + break; + + case NSLeftMouseUp: + case NSRightMouseUp: + case NSOtherMouseUp: + event.type = EV_KeyUp; + break; + + default: + break; + } + + event.data1 = std::min(KEY_MOUSE1 + [theEvent buttonNumber], NSInteger(KEY_MOUSE8)); + + D_PostEvent(&event); + } +} + + +void ProcessMouseMoveInMenu(NSEvent* theEvent) +{ + event_t event = {}; + + event.type = EV_GUI_Event; + event.subtype = EV_GUI_MouseMove; + + NSEventToGameMousePosition(theEvent, &event); + + D_PostEvent(&event); +} + +void ProcessMouseMoveInGame(NSEvent* theEvent) +{ + if (!use_mouse) + { + return; + } + + // TODO: remove this magic! + + if (s_skipMouseMoves > 0) + { + --s_skipMouseMoves; + return; + } + + int x([theEvent deltaX]); + int y(-[theEvent deltaY]); + + if (0 == x && 0 == y) + { + return; + } + + if (!m_noprescale) + { + x *= 3; + y *= 2; + } + + event_t event = {}; + + static int lastX = 0, lastY = 0; + + if (m_filter) + { + event.x = (x + lastX) / 2; + event.y = (y + lastY) / 2; + } + else + { + event.x = x; + event.y = y; + } + + lastX = x; + lastY = y; + + if (0 != event.x | 0 != event.y) + { + event.type = EV_Mouse; + + D_PostEvent(&event); + } +} + +void ProcessMouseMoveEvent(NSEvent* theEvent) +{ + if (GUICapture) + { + ProcessMouseMoveInMenu(theEvent); + } + else + { + ProcessMouseMoveInGame(theEvent); + } +} + + +void ProcessMouseWheelEvent(NSEvent* theEvent) +{ + const CGFloat delta = [theEvent deltaY]; + const bool isZeroDelta = fabs(delta) < 1.0E-5; + + if (isZeroDelta && GUICapture) + { + return; + } + + event_t event = {}; + + if (GUICapture) + { + event.type = EV_GUI_Event; + event.subtype = delta > 0.0f ? EV_GUI_WheelUp : EV_GUI_WheelDown; + event.data3 = delta; + event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent); + } + else + { + event.type = isZeroDelta ? EV_KeyUp : EV_KeyDown; + event.data1 = delta > 0.0f ? KEY_MWHEELUP : KEY_MWHEELDOWN; + } + + D_PostEvent(&event); +} + + +const Uint16 BYTES_PER_PIXEL = 4; + +} // unnamed namespace + + +// --------------------------------------------------------------------------- + + +namespace +{ + const NSInteger LEVEL_FULLSCREEN = NSMainMenuWindowLevel + 1; + const NSInteger LEVEL_WINDOWED = NSNormalWindowLevel; + + const NSUInteger STYLE_MASK_FULLSCREEN = NSBorderlessWindowMask; + const NSUInteger STYLE_MASK_WINDOWED = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; +} + + +// --------------------------------------------------------------------------- + + +@interface FullscreenWindow : NSWindow +{ + +} + +- (bool)canBecomeKeyWindow; + +- (void)setLevel:(NSInteger)level; +- (void)setStyleMask:(NSUInteger)styleMask; + +@end + + +// --------------------------------------------------------------------------- + + +@interface FullscreenView : NSOpenGLView +{ + +} + +- (void)resetCursorRects; + +@end + + +@implementation FullscreenView + +- (void)resetCursorRects +{ + [super resetCursorRects]; + [self addCursorRect: [self bounds] + cursor: s_cursor]; +} + +@end + + +// --------------------------------------------------------------------------- + + +@interface ApplicationController : NSResponder +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + +#endif +{ +@private + FullscreenWindow* m_window; + + uint8_t* m_softwareRenderingBuffer; + GLuint m_softwareRenderingTexture; + + int m_multisample; + + int m_width; + int m_height; + bool m_fullscreen; + bool m_hiDPI; + + bool m_openGLInitialized; +} + +- (id)init; +- (void)dealloc; + +- (void)keyDown:(NSEvent*)theEvent; +- (void)keyUp:(NSEvent*)theEvent; + +- (void)applicationDidBecomeActive:(NSNotification*)aNotification; +- (void)applicationWillResignActive:(NSNotification*)aNotification; + +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification; + +- (void)applicationWillTerminate:(NSNotification*)aNotification; + +- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename; + +- (int)multisample; +- (void)setMultisample:(int)multisample; + +- (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height useHiDPI:(bool)hiDPI; +- (void)useHiDPI:(bool)hiDPI; + +- (void)setupSoftwareRenderingWithWidth:(int)width height:(int)height; +- (void*)softwareRenderingBuffer; + +- (void)processEvents:(NSTimer*)timer; + +- (void)invalidateCursorRects; + +- (void)setMainWindowVisible:(bool)visible; + +- (void)setWindowStyleMask:(NSUInteger)styleMask; + +@end + + +static ApplicationController* appCtrl; + + +// --------------------------------------------------------------------------- + + +@implementation FullscreenWindow + +static bool s_fullscreenNewAPI; + ++ (void)initialize +{ + // The following value shoud be equal to NSAppKitVersionNumber10_6 + // and it's hard-coded in order to build on earlier SDKs + s_fullscreenNewAPI = NSAppKitVersionNumber >= 1038; +} + +- (bool)canBecomeKeyWindow +{ + return true; +} + +- (void)setLevel:(NSInteger)level +{ + if (s_fullscreenNewAPI) + { + [super setLevel:level]; + } + else + { + // Old Carbon-based way to make fullscreen window above dock and menu + // It's supported on 64-bit, but on 10.6 and later the following is preferred: + // [NSWindow setLevel:NSMainMenuWindowLevel + 1] + + const SystemUIMode mode = LEVEL_FULLSCREEN == level + ? kUIModeAllHidden + : kUIModeNormal; + SetSystemUIMode(mode, 0); + } +} + +- (void)setStyleMask:(NSUInteger)styleMask +{ + if (s_fullscreenNewAPI) + { + [super setStyleMask:styleMask]; + } + else + { + [appCtrl setWindowStyleMask:styleMask]; + } +} + +@end + + +// --------------------------------------------------------------------------- + + +@implementation ApplicationController + +- (id)init +{ + self = [super init]; + + m_window = nil; + + m_softwareRenderingBuffer = NULL; + m_softwareRenderingTexture = 0; + + m_multisample = 0; + + m_width = -1; + m_height = -1; + m_fullscreen = false; + m_hiDPI = false; + + m_openGLInitialized = false; + + return self; +} + +- (void)dealloc +{ + delete[] m_softwareRenderingBuffer; + + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &m_softwareRenderingTexture); + + [m_window release]; + + [super dealloc]; +} + + +- (void)keyDown:(NSEvent*)theEvent +{ + // Empty but present to avoid playing of 'beep' alert sound + + ZD_UNUSED(theEvent); +} + +- (void)keyUp:(NSEvent*)theEvent +{ + // Empty but present to avoid playing of 'beep' alert sound + + ZD_UNUSED(theEvent); +} + + +- (void)applicationDidBecomeActive:(NSNotification*)aNotification +{ + ZD_UNUSED(aNotification); + + S_SetSoundPaused(1); +} + +- (void)applicationWillResignActive:(NSNotification*)aNotification +{ + ZD_UNUSED(aNotification); + + S_SetSoundPaused(0); +} + + +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification +{ + // When starting from command line with real executable path, e.g. ZDoom.app/Contents/MacOS/ZDoom + // application remains deactivated for an unknown reason. + // The following call resolves this issue + [NSApp activateIgnoringOtherApps:YES]; + + // Setup timer for custom event loop + + NSTimer* timer = [NSTimer timerWithTimeInterval:0 + target:self + selector:@selector(processEvents:) + userInfo:nil + repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:timer + forMode:NSDefaultRunLoopMode]; + + exit(SDL_main(s_argc, s_argv)); +} + + +- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename +{ + ZD_UNUSED(theApplication); + + if (s_restartedFromWADPicker + || 0 == [filename length] + || s_argc + 2 >= ARGC_MAX) + { + return FALSE; + } + + // Some parameters from command line are passed to this function + // These parameters need to be skipped to avoid duplication + // Note: SDL has different approach to fix this issue, see the same method in SDLMain.m + + const char* const charFileName = [filename UTF8String]; + + for (int i = 0; i < s_argc; ++i) + { + if (0 == strcmp(s_argv[i], charFileName)) + { + return FALSE; + } + } + + s_argvStorage.Push("-file"); + s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); + + s_argvStorage.Push([filename UTF8String]); + s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); + + return TRUE; +} + + +- (void)applicationWillTerminate:(NSNotification*)aNotification +{ + ZD_UNUSED(aNotification); + + // Hide window as nothing will be rendered at this point + [m_window orderOut:nil]; + + I_ShutdownJoysticks(); +} + + +- (int)multisample +{ + return m_multisample; +} + +- (void)setMultisample:(int)multisample +{ + m_multisample = multisample; +} + + +- (FullscreenWindow*)createWindow:(NSUInteger)styleMask +{ + FullscreenWindow* window = [[FullscreenWindow alloc] initWithContentRect:NSMakeRect(0, 0, 640, 480) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + [window setOpaque:YES]; + [window makeFirstResponder:self]; + [window setAcceptsMouseMovedEvents:YES]; + + return window; +} + +- (void)initializeOpenGL +{ + if (m_openGLInitialized) + { + return; + } + + m_window = [self createWindow:STYLE_MASK_WINDOWED]; + + // Create OpenGL context and view + + NSOpenGLPixelFormatAttribute attributes[16]; + size_t i = 0; + + attributes[i++] = NSOpenGLPFADoubleBuffer; + attributes[i++] = NSOpenGLPFAColorSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(32); + attributes[i++] = NSOpenGLPFADepthSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(24); + attributes[i++] = NSOpenGLPFAStencilSize; + attributes[i++] = NSOpenGLPixelFormatAttribute(8); + + if (m_multisample) + { + attributes[i++] = NSOpenGLPFAMultisample; + attributes[i++] = NSOpenGLPFASampleBuffers; + attributes[i++] = NSOpenGLPixelFormatAttribute(1); + attributes[i++] = NSOpenGLPFASamples; + attributes[i++] = NSOpenGLPixelFormatAttribute(m_multisample); + } + + attributes[i] = NSOpenGLPixelFormatAttribute(0); + + NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + + const NSRect contentRect = [m_window contentRectForFrameRect:[m_window frame]]; + NSOpenGLView* glView = [[FullscreenView alloc] initWithFrame:contentRect + pixelFormat:pixelFormat]; + [[glView openGLContext] makeCurrentContext]; + + [m_window setContentView:glView]; + + m_openGLInitialized = true; +} + +- (void)setFullscreenModeWidth:(int)width height:(int)height +{ + NSScreen* screen = [m_window screen]; + + const NSRect screenFrame = [screen frame]; + const NSRect displayRect = vid_hidpi + ? [screen convertRectToBacking:screenFrame] + : screenFrame; + + const float displayWidth = displayRect.size.width; + const float displayHeight = displayRect.size.height; + + const float pixelScaleFactorX = displayWidth / static_cast(width ); + const float pixelScaleFactorY = displayHeight / static_cast(height); + + rbOpts.pixelScale = std::min(pixelScaleFactorX, pixelScaleFactorY); + + rbOpts.width = width * rbOpts.pixelScale; + rbOpts.height = height * rbOpts.pixelScale; + + rbOpts.shiftX = (displayWidth - rbOpts.width ) / 2.0f; + rbOpts.shiftY = (displayHeight - rbOpts.height) / 2.0f; + + if (!m_fullscreen) + { + [m_window setLevel:LEVEL_FULLSCREEN]; + [m_window setStyleMask:STYLE_MASK_FULLSCREEN]; + [m_window setHidesOnDeactivate:YES]; + } + + [m_window setFrame:displayRect display:YES]; + [m_window setFrameOrigin:NSMakePoint(0.0f, 0.0f)]; +} + +- (void)setWindowedModeWidth:(int)width height:(int)height +{ + rbOpts.pixelScale = 1.0f; + + rbOpts.width = static_cast(width ); + rbOpts.height = static_cast(height); + + rbOpts.shiftX = 0.0f; + rbOpts.shiftY = 0.0f; + + const NSSize windowPixelSize = NSMakeSize(width, height); + const NSSize windowSize = vid_hidpi + ? [[m_window contentView] convertSizeFromBacking:windowPixelSize] + : windowPixelSize; + + if (m_fullscreen) + { + [m_window setLevel:LEVEL_WINDOWED]; + [m_window setStyleMask:STYLE_MASK_WINDOWED]; + [m_window setHidesOnDeactivate:NO]; + } + + [m_window setContentSize:windowSize]; + [m_window center]; + + NSButton* closeButton = [m_window standardWindowButton:NSWindowCloseButton]; + [closeButton setAction:@selector(terminate:)]; + [closeButton setTarget:NSApp]; +} + +- (void)changeVideoResolution:(bool)fullscreen width:(int)width height:(int)height useHiDPI:(bool)hiDPI +{ + if (fullscreen == m_fullscreen + && width == m_width + && height == m_height + && hiDPI == m_hiDPI) + { + return; + } + + [self initializeOpenGL]; + + if (IsHiDPISupported()) + { + NSOpenGLView* const glView = [m_window contentView]; + [glView setWantsBestResolutionOpenGLSurface:hiDPI]; + } + + if (fullscreen) + { + [self setFullscreenModeWidth:width height:height]; + } + else + { + [self setWindowedModeWidth:width height:height]; + } + + rbOpts.dirty = true; + + const NSSize viewSize = GetRealContentViewSize(m_window); + + glViewport(0, 0, static_cast(viewSize.width), static_cast(viewSize.height)); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + CGLFlushDrawable(CGLGetCurrentContext()); + + static NSString* const TITLE_STRING = + [NSString stringWithFormat:@"%s %s", GAMESIG, GetVersionString()]; + [m_window setTitle:TITLE_STRING]; + + if (![m_window isKeyWindow]) + { + [m_window makeKeyAndOrderFront:nil]; + } + + m_fullscreen = fullscreen; + m_width = width; + m_height = height; + m_hiDPI = hiDPI; +} + +- (void)useHiDPI:(bool)hiDPI +{ + if (!m_openGLInitialized) + { + return; + } + + [self changeVideoResolution:m_fullscreen + width:m_width + height:m_height + useHiDPI:hiDPI]; +} + + +- (void)setupSoftwareRenderingWithWidth:(int)width height:(int)height +{ + if (0 == m_softwareRenderingTexture) + { + glEnable(GL_TEXTURE_RECTANGLE_ARB); + + glGenTextures(1, &m_softwareRenderingTexture); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_softwareRenderingTexture); + glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); + + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + delete[] m_softwareRenderingBuffer; + m_softwareRenderingBuffer = new uint8_t[width * height * BYTES_PER_PIXEL]; + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, width, height, 0.0, -1.0, 1.0); +} + +- (void*)softwareRenderingBuffer +{ + return m_softwareRenderingBuffer; +} + + +- (void)processEvents:(NSTimer*)timer +{ + ZD_UNUSED(timer); + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + while (true) + { + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate dateWithTimeIntervalSinceNow:0] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (nil == event) + { + break; + } + + const NSEventType eventType = [event type]; + + switch (eventType) + { + case NSMouseMoved: + ProcessMouseMoveEvent(event); + break; + + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSRightMouseDown: + case NSRightMouseUp: + case NSOtherMouseDown: + case NSOtherMouseUp: + ProcessMouseButtonEvent(event); + break; + + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + ProcessMouseButtonEvent(event); + ProcessMouseMoveEvent(event); + break; + + case NSScrollWheel: + ProcessMouseWheelEvent(event); + break; + + case NSKeyDown: + case NSKeyUp: + ProcessKeyboardEvent(event); + break; + + case NSFlagsChanged: + ProcessKeyboardFlagsEvent(event); + break; + + default: + break; + } + + [NSApp sendEvent:event]; + } + + [NSApp updateWindows]; + + [pool release]; +} + + +- (void)invalidateCursorRects +{ + [m_window invalidateCursorRectsForView:[m_window contentView]]; +} + + +- (void)setMainWindowVisible:(bool)visible +{ + if (visible) + { + [m_window orderFront:nil]; + } + else + { + [m_window orderOut:nil]; + } +} + + +- (void)setWindowStyleMask:(NSUInteger)styleMask +{ + // Before 10.6 it's impossible to change window's style mask + // To workaround this new window should be created with required style mask + // This method should not be called when building for Snow Leopard or newer + + FullscreenWindow* tempWindow = [self createWindow:styleMask]; + [tempWindow setContentView:[m_window contentView]]; + + [m_window close]; + m_window = tempWindow; +} + +@end + + +// --------------------------------------------------------------------------- + + +CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (IsHiDPISupported()) + { + [appCtrl useHiDPI:self]; + } + else if (0 != self) + { + self = 0; + } +} + + +// --------------------------------------------------------------------------- + + +void I_SetMainWindowVisible(bool visible) +{ + [appCtrl setMainWindowVisible:visible]; + + SetNativeMouse(!visible); +} + + +// --------------------------------------------------------------------------- + + +bool I_SetCursor(FTexture* cursorpic) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + if (NULL == cursorpic || FTexture::TEX_Null == cursorpic->UseType) + { + s_cursor = [NSCursor arrowCursor]; + } + else + { + // Create bitmap image representation + + const NSInteger imageWidth = cursorpic->GetWidth(); + const NSInteger imageHeight = cursorpic->GetHeight(); + const NSInteger imagePitch = imageWidth * 4; + + NSBitmapImageRep* bitmapImageRep = [NSBitmapImageRep alloc]; + [bitmapImageRep initWithBitmapDataPlanes:NULL + pixelsWide:imageWidth + pixelsHigh:imageHeight + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:imagePitch + bitsPerPixel:0]; + + // Load bitmap data to representation + + BYTE* buffer = [bitmapImageRep bitmapData]; + memset(buffer, 0, imagePitch * imageHeight); + + FBitmap bitmap(buffer, imagePitch, imageWidth, imageHeight); + cursorpic->CopyTrueColorPixels(&bitmap, 0, 0); + + // Swap red and blue components in each pixel + + for (size_t i = 0; i < size_t(imageWidth * imageHeight); ++i) + { + const size_t offset = i * 4; + + const BYTE temp = buffer[offset ]; + buffer[offset ] = buffer[offset + 2]; + buffer[offset + 2] = temp; + } + + // Create image from representation and set it as cursor + + NSData* imageData = [bitmapImageRep representationUsingType:NSPNGFileType + properties:nil]; + NSImage* cursorImage = [[NSImage alloc] initWithData:imageData]; + + s_cursor = [[NSCursor alloc] initWithImage:cursorImage + hotSpot:NSMakePoint(0.0f, 0.0f)]; + } + + [appCtrl invalidateCursorRects]; + + [pool release]; + + return true; +} + + +// --------------------------------------------------------------------------- + + +const char* I_GetBackEndName() +{ + return "Native Cocoa"; +} + + +// --------------------------------------------------------------------------- + + +extern "C" +{ + +struct SDL_mutex +{ + pthread_mutex_t mutex; +}; + + +SDL_mutex* SDL_CreateMutex() +{ + pthread_mutexattr_t attributes; + pthread_mutexattr_init(&attributes); + pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE); + + SDL_mutex* result = new SDL_mutex; + + if (0 != pthread_mutex_init(&result->mutex, &attributes)) + { + delete result; + result = NULL; + } + + pthread_mutexattr_destroy(&attributes); + + return result; +} + +int SDL_mutexP(SDL_mutex* mutex) +{ + return pthread_mutex_lock(&mutex->mutex); +} + +int SDL_mutexV(SDL_mutex* mutex) +{ + return pthread_mutex_unlock(&mutex->mutex); +} + +void SDL_DestroyMutex(SDL_mutex* mutex) +{ + pthread_mutex_destroy(&mutex->mutex); + delete mutex; +} + + +static timeval s_startTicks; + +uint32_t SDL_GetTicks() +{ + timeval now; + gettimeofday(&now, NULL); + + const uint32_t ticks = + (now.tv_sec - s_startTicks.tv_sec ) * 1000 + + (now.tv_usec - s_startTicks.tv_usec) / 1000; + + return ticks; +} + + +int SDL_Init(Uint32 flags) +{ + ZD_UNUSED(flags); + + return 0; +} + +void SDL_Quit() +{ + if (NULL != appCtrl) + { + [NSApp setDelegate:nil]; + [NSApp deactivate]; + + [appCtrl release]; + appCtrl = NULL; + } +} + + +char* SDL_GetError() +{ + static char empty[] = {0}; + return empty; +} + + +char* SDL_VideoDriverName(char* namebuf, int maxlen) +{ + return strncpy(namebuf, "Native OpenGL", maxlen); +} + +const SDL_VideoInfo* SDL_GetVideoInfo() +{ + // NOTE: Only required fields are assigned + + static SDL_PixelFormat pixelFormat; + memset(&pixelFormat, 0, sizeof(pixelFormat)); + + pixelFormat.BitsPerPixel = 32; + + static SDL_VideoInfo videoInfo; + memset(&videoInfo, 0, sizeof(videoInfo)); + + const NSRect displayRect = [[NSScreen mainScreen] frame]; + + videoInfo.current_w = displayRect.size.width; + videoInfo.current_h = displayRect.size.height; + videoInfo.vfmt = &pixelFormat; + + return &videoInfo; +} + +SDL_Rect** SDL_ListModes(SDL_PixelFormat* format, Uint32 flags) +{ + ZD_UNUSED(format); + ZD_UNUSED(flags); + + static std::vector resolutions; + + if (resolutions.empty()) + { +#define DEFINE_RESOLUTION(WIDTH, HEIGHT) \ + static SDL_Rect resolution_##WIDTH##_##HEIGHT = { 0, 0, WIDTH, HEIGHT }; \ + resolutions.push_back(&resolution_##WIDTH##_##HEIGHT); + + DEFINE_RESOLUTION( 640, 480); + DEFINE_RESOLUTION( 720, 480); + DEFINE_RESOLUTION( 800, 480); + DEFINE_RESOLUTION( 800, 600); + DEFINE_RESOLUTION(1024, 600); + DEFINE_RESOLUTION(1024, 640); + DEFINE_RESOLUTION(1024, 768); + DEFINE_RESOLUTION(1152, 720); + DEFINE_RESOLUTION(1152, 864); + DEFINE_RESOLUTION(1280, 720); + DEFINE_RESOLUTION(1280, 768); + DEFINE_RESOLUTION(1280, 800); + DEFINE_RESOLUTION(1280, 854); + DEFINE_RESOLUTION(1280, 960); + DEFINE_RESOLUTION(1280, 1024); + DEFINE_RESOLUTION(1366, 768); + DEFINE_RESOLUTION(1400, 1050); + DEFINE_RESOLUTION(1440, 900); + DEFINE_RESOLUTION(1440, 960); + DEFINE_RESOLUTION(1440, 1080); + DEFINE_RESOLUTION(1600, 900); + DEFINE_RESOLUTION(1600, 1200); + DEFINE_RESOLUTION(1680, 1050); + DEFINE_RESOLUTION(1920, 1080); + DEFINE_RESOLUTION(1920, 1200); + DEFINE_RESOLUTION(2048, 1080); + DEFINE_RESOLUTION(2048, 1536); + DEFINE_RESOLUTION(2560, 1080); + DEFINE_RESOLUTION(2560, 1440); + DEFINE_RESOLUTION(2560, 1600); + DEFINE_RESOLUTION(2560, 2048); + DEFINE_RESOLUTION(2880, 1800); + DEFINE_RESOLUTION(3200, 1800); + DEFINE_RESOLUTION(3440, 1440); + DEFINE_RESOLUTION(3840, 2160); + DEFINE_RESOLUTION(3840, 2400); + DEFINE_RESOLUTION(4096, 2160); + DEFINE_RESOLUTION(5120, 2880); + +#undef DEFINE_RESOLUTION + + resolutions.push_back(NULL); + } + + return &resolutions[0]; +} + +int SDL_ShowCursor(int) +{ + // Does nothing + return 0; +} + + +static SDL_PixelFormat* GetPixelFormat() +{ + static SDL_PixelFormat result; + + result.palette = NULL; + result.BitsPerPixel = BYTES_PER_PIXEL * 8; + result.BytesPerPixel = BYTES_PER_PIXEL; + result.Rloss = 0; + result.Gloss = 0; + result.Bloss = 0; + result.Aloss = 8; + result.Rshift = 8; + result.Gshift = 16; + result.Bshift = 24; + result.Ashift = 0; + result.Rmask = 0x000000FF; + result.Gmask = 0x0000FF00; + result.Bmask = 0x00FF0000; + result.Amask = 0xFF000000; + result.colorkey = 0; + result.alpha = 0xFF; + + return &result; +} + + +SDL_Surface* SDL_SetVideoMode(int width, int height, int, Uint32 flags) +{ + [appCtrl changeVideoResolution:(SDL_FULLSCREEN & flags) + width:width + height:height + useHiDPI:vid_hidpi]; + + static SDL_Surface result; + + if (!(SDL_OPENGL & flags)) + { + [appCtrl setupSoftwareRenderingWithWidth:width + height:height]; + } + + result.flags = flags; + result.format = GetPixelFormat(); + result.w = width; + result.h = height; + result.pitch = width * BYTES_PER_PIXEL; + result.pixels = [appCtrl softwareRenderingBuffer]; + result.refcount = 1; + + result.clip_rect.x = 0; + result.clip_rect.y = 0; + result.clip_rect.w = width; + result.clip_rect.h = height; + + return &result; +} + + +void SDL_WM_SetCaption(const char* title, const char* icon) +{ + ZD_UNUSED(title); + ZD_UNUSED(icon); + + // Window title is set in SDL_SetVideoMode() +} + +int SDL_WM_ToggleFullScreen(SDL_Surface* surface) +{ + if (surface->flags & SDL_FULLSCREEN) + { + surface->flags &= ~SDL_FULLSCREEN; + } + else + { + surface->flags |= SDL_FULLSCREEN; + } + + [appCtrl changeVideoResolution:(SDL_FULLSCREEN & surface->flags) + width:surface->w + height:surface->h + useHiDPI:vid_hidpi]; + + return 1; +} + + +void SDL_GL_SwapBuffers() +{ + [[NSOpenGLContext currentContext] flushBuffer]; +} + +int SDL_GL_SetAttribute(SDL_GLattr attr, int value) +{ + if (SDL_GL_MULTISAMPLESAMPLES == attr) + { + [appCtrl setMultisample:value]; + } + + // Not interested in other attributes + + return 0; +} + + +int SDL_LockSurface(SDL_Surface* surface) +{ + ZD_UNUSED(surface); + + return 0; +} + +void SDL_UnlockSurface(SDL_Surface* surface) +{ + ZD_UNUSED(surface); +} + +int SDL_BlitSurface(SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect) +{ + ZD_UNUSED(src); + ZD_UNUSED(srcrect); + ZD_UNUSED(dst); + ZD_UNUSED(dstrect); + + return 0; +} + + +int SDL_Flip(SDL_Surface* screen) +{ + assert(NULL != screen); + + if (rbOpts.dirty) + { + glViewport(rbOpts.shiftX, rbOpts.shiftY, rbOpts.width, rbOpts.height); + + // TODO: Figure out why the following glClear() call is needed + // to avoid drawing of garbage in fullscreen mode when + // in-game's aspect ratio is different from display one + glClear(GL_COLOR_BUFFER_BIT); + + rbOpts.dirty = false; + } + + const int width = screen->w; + const int height = screen->h; + +#ifdef __LITTLE_ENDIAN__ + static const GLenum format = GL_RGBA; +#else // __BIG_ENDIAN__ + static const GLenum format = GL_ABGR_EXT; +#endif // __LITTLE_ENDIAN__ + + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, + width, height, 0, format, GL_UNSIGNED_BYTE, screen->pixels); + + glBegin(GL_QUADS); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(0.0f, 0.0f); + glTexCoord2f(width, 0.0f); + glVertex2f(width, 0.0f); + glTexCoord2f(width, height); + glVertex2f(width, height); + glTexCoord2f(0.0f, height); + glVertex2f(0.0f, height); + glEnd(); + + glFlush(); + + SDL_GL_SwapBuffers(); + + return 0; +} + +int SDL_SetPalette(SDL_Surface* surface, int flags, SDL_Color* colors, int firstcolor, int ncolors) +{ + ZD_UNUSED(surface); + ZD_UNUSED(flags); + ZD_UNUSED(colors); + ZD_UNUSED(firstcolor); + ZD_UNUSED(ncolors); + + return 0; +} + +} // extern "C" + + +namespace +{ + +NSMenuItem* CreateApplicationMenu() +{ + NSMenu* menu = [NSMenu new]; + + [menu addItemWithTitle:[@"About " stringByAppendingString:@GAMENAME] + action:@selector(orderFrontStandardAboutPanel:) + keyEquivalent:@""]; + [menu addItem:[NSMenuItem separatorItem]]; + [menu addItemWithTitle:[@"Hide " stringByAppendingString:@GAMENAME] + action:@selector(hide:) + keyEquivalent:@"h"]; + [[menu addItemWithTitle:@"Hide Others" + action:@selector(hideOtherApplications:) + keyEquivalent:@"h"] + setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask]; + [menu addItemWithTitle:@"Show All" + action:@selector(unhideAllApplications:) + keyEquivalent:@""]; + [menu addItem:[NSMenuItem separatorItem]]; + [menu addItemWithTitle:[@"Quit " stringByAppendingString:@GAMENAME] + action:@selector(terminate:) + keyEquivalent:@"q"]; + + NSMenuItem* menuItem = [NSMenuItem new]; + [menuItem setSubmenu:menu]; + + if ([NSApp respondsToSelector:@selector(setAppleMenu:)]) + { + [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; + } + + return menuItem; +} + +NSMenuItem* CreateEditMenu() +{ + NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Edit"]; + + [menu addItemWithTitle:@"Undo" + action:@selector(undo:) + keyEquivalent:@"z"]; + [menu addItemWithTitle:@"Redo" + action:@selector(redo:) + keyEquivalent:@"Z"]; + [menu addItem:[NSMenuItem separatorItem]]; + [menu addItemWithTitle:@"Cut" + action:@selector(cut:) + keyEquivalent:@"x"]; + [menu addItemWithTitle:@"Copy" + action:@selector(copy:) + keyEquivalent:@"c"]; + [menu addItemWithTitle:@"Paste" + action:@selector(paste:) + keyEquivalent:@"v"]; + [menu addItemWithTitle:@"Delete" + action:@selector(delete:) + keyEquivalent:@""]; + [menu addItemWithTitle:@"Select All" + action:@selector(selectAll:) + keyEquivalent:@"a"]; + + NSMenuItem* menuItem = [NSMenuItem new]; + [menuItem setSubmenu:menu]; + + return menuItem; +} + +NSMenuItem* CreateWindowMenu() +{ + NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Window"]; + [NSApp setWindowsMenu:menu]; + + [menu addItemWithTitle:@"Minimize" + action:@selector(performMiniaturize:) + keyEquivalent:@"m"]; + [menu addItemWithTitle:@"Zoom" + action:@selector(performZoom:) + keyEquivalent:@""]; + [menu addItem:[NSMenuItem separatorItem]]; + [menu addItemWithTitle:@"Bring All to Front" + action:@selector(arrangeInFront:) + keyEquivalent:@""]; + + NSMenuItem* menuItem = [NSMenuItem new]; + [menuItem setSubmenu:menu]; + + return menuItem; +} + +void CreateMenu() +{ + NSMenu* menuBar = [NSMenu new]; + [menuBar addItem:CreateApplicationMenu()]; + [menuBar addItem:CreateEditMenu()]; + [menuBar addItem:CreateWindowMenu()]; + + [NSApp setMainMenu:menuBar]; +} + +DarwinVersion GetDarwinVersion() +{ + DarwinVersion result = {}; + + int mib[2] = { CTL_KERN, KERN_OSRELEASE }; + size_t size = 0; + + if (0 == sysctl(mib, 2, NULL, &size, NULL, 0)) + { + char* version = static_cast(alloca(size)); + + if (0 == sysctl(mib, 2, version, &size, NULL, 0)) + { + sscanf(version, "%hu.%hu.%hu", + &result.major, &result.minor, &result.bugfix); + } + } + + return result; +} + +} // unnamed namespace + + +const DarwinVersion darwinVersion = GetDarwinVersion(); + + +#ifdef main +#undef main +#endif // main + +int main(int argc, char** argv) +{ + gettimeofday(&s_startTicks, NULL); + + for (int i = 0; i <= argc; ++i) + { + const char* const argument = argv[i]; + + if (NULL == argument || '\0' == argument[0]) + { + continue; + } + + if (0 == strcmp(argument, "-wad_picker_restart")) + { + s_restartedFromWADPicker = true; + } + else + { + s_argvStorage.Push(argument); + s_argv[s_argc++] = s_argvStorage.Last().LockBuffer(); + } + } + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + [NSApplication sharedApplication]; + + // The following code isn't mandatory, + // but it enables to run the application without a bundle + if ([NSApp respondsToSelector:@selector(setActivationPolicy:)]) + { + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + } + + CreateMenu(); + + appCtrl = [ApplicationController new]; + [NSApp setDelegate:appCtrl]; + + [NSApp run]; + + [pool release]; + + return EXIT_SUCCESS; +} diff --git a/src/cocoa/i_joystick.cpp b/src/cocoa/i_joystick.cpp new file mode 100644 index 000000000..56db8f815 --- /dev/null +++ b/src/cocoa/i_joystick.cpp @@ -0,0 +1,820 @@ +/* + ** i_joystick.cpp + ** + **--------------------------------------------------------------------------- + ** Copyright 2012-2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include "m_joy.h" + +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 + +#include "HID_Utilities_External.h" + +#include "d_event.h" +#include "doomdef.h" +#include "templates.h" +#include "i_osversion.h" + + +namespace +{ + +FString ToFString( const CFStringRef string ) +{ + if ( NULL == string ) + { + return FString(); + } + + const CFIndex stringLength = CFStringGetLength( string ); + + if ( 0 == stringLength ) + { + return FString(); + } + + const size_t bufferSize = CFStringGetMaximumSizeForEncoding( stringLength, kCFStringEncodingUTF8 ) + 1; + + char buffer[ bufferSize ]; + memset( buffer, 0, bufferSize ); + + CFStringGetCString( string, buffer, bufferSize, kCFStringEncodingUTF8 ); + + return FString( buffer ); +} + + +class IOKitJoystick : public IJoystickConfig +{ +public: + explicit IOKitJoystick( IOHIDDeviceRef device ); + virtual ~IOKitJoystick(); + + virtual FString GetName(); + virtual float GetSensitivity(); + virtual void SetSensitivity( float scale ); + + virtual int GetNumAxes(); + virtual float GetAxisDeadZone( int axis ); + virtual EJoyAxis GetAxisMap( int axis ); + virtual const char* GetAxisName( int axis ); + virtual float GetAxisScale( int axis ); + + virtual void SetAxisDeadZone( int axis, float deadZone ); + virtual void SetAxisMap( int axis, EJoyAxis gameAxis ); + virtual void SetAxisScale( int axis, float scale ); + + virtual bool IsSensitivityDefault(); + virtual bool IsAxisDeadZoneDefault( int axis ); + virtual bool IsAxisMapDefault( int axis ); + virtual bool IsAxisScaleDefault( int axis ); + + virtual void SetDefaultConfig(); + virtual FString GetIdentifier(); + + void AddAxes( float axes[ NUM_JOYAXIS ] ) const; + + void Update(); + +private: + IOHIDDeviceRef m_device; + + float m_sensitivity; + + struct AxisInfo + { + char name[ 64 ]; + + float value; + + float deadZone; + float defaultDeadZone; + float sensitivity; + float defaultSensitivity; + + EJoyAxis gameAxis; + EJoyAxis defaultGameAxis; + + IOHIDElementRef element; + }; + + TArray< AxisInfo > m_axes; + + TArray< IOHIDElementRef > m_buttons; + TArray< IOHIDElementRef > m_POVs; + + + static const float DEFAULT_DEADZONE; + static const float DEFAULT_SENSITIVITY; + + + bool ProcessAxis ( const IOHIDValueRef value ); + bool ProcessButton( const IOHIDValueRef value ); + bool ProcessPOV ( const IOHIDValueRef value ); + +}; + + +const float IOKitJoystick::DEFAULT_DEADZONE = 0.25f; +const float IOKitJoystick::DEFAULT_SENSITIVITY = 1.0f; + + +IOKitJoystick::IOKitJoystick( IOHIDDeviceRef device ) +: m_device( device ) +, m_sensitivity( DEFAULT_SENSITIVITY ) +{ + IOHIDElementRef element = HIDGetFirstDeviceElement( device, kHIDElementTypeInput ); + + while ( NULL != element ) + { + const uint32_t usagePage = IOHIDElementGetUsagePage( element ); + + if ( kHIDPage_GenericDesktop == usagePage ) + { + const uint32_t usage = IOHIDElementGetUsage( element ); + + if ( kHIDUsage_GD_Slider == usage + || kHIDUsage_GD_X == usage || kHIDUsage_GD_Y == usage || kHIDUsage_GD_Z == usage + || kHIDUsage_GD_Rx == usage || kHIDUsage_GD_Ry == usage || kHIDUsage_GD_Rz == usage ) + { + AxisInfo axis; + memset( &axis, 0, sizeof( axis ) ); + + if ( const CFStringRef name = IOHIDElementGetName( element ) ) + { + CFStringGetCString( name, axis.name, sizeof( axis.name ) - 1, kCFStringEncodingUTF8 ); + } + else + { + snprintf( axis.name, sizeof( axis.name ), "Axis %i", m_axes.Size() + 1 ); + } + + axis.element = element; + + m_axes.Push( axis ); + + IOHIDElement_SetCalibrationMin( element, -1 ); + IOHIDElement_SetCalibrationMax( element, 1 ); + + HIDQueueElement( m_device, element ); + } + else if ( kHIDUsage_GD_Hatswitch == usage && m_POVs.Size() < 4 ) + { + m_POVs.Push( element ); + + HIDQueueElement( m_device, element ); + } + } + else if ( kHIDPage_Button == usagePage ) + { + m_buttons.Push( element ); + + HIDQueueElement( m_device, element ); + } + + element = HIDGetNextDeviceElement( element, kHIDElementTypeInput ); + } + + SetDefaultConfig(); +} + +IOKitJoystick::~IOKitJoystick() +{ + M_SaveJoystickConfig( this ); +} + + +FString IOKitJoystick::GetName() +{ + FString result; + + result += ToFString( IOHIDDevice_GetManufacturer( m_device ) ); + result += " "; + result += ToFString( IOHIDDevice_GetProduct( m_device ) ); + + return result; +} + + +float IOKitJoystick::GetSensitivity() +{ + return m_sensitivity; +} + +void IOKitJoystick::SetSensitivity( float scale ) +{ + m_sensitivity = scale; +} + + +int IOKitJoystick::GetNumAxes() +{ + return static_cast< int >( m_axes.Size() ); +} + +#define IS_AXIS_VALID ( static_cast< unsigned int >( axis ) < m_axes.Size() ) + +float IOKitJoystick::GetAxisDeadZone( int axis ) +{ + return IS_AXIS_VALID ? m_axes[ axis ].deadZone : 0.0f; +} + +EJoyAxis IOKitJoystick::GetAxisMap( int axis ) +{ + return IS_AXIS_VALID ? m_axes[ axis ].gameAxis : JOYAXIS_None; +} + +const char* IOKitJoystick::GetAxisName( int axis ) +{ + return IS_AXIS_VALID ? m_axes[ axis ].name : "Invalid"; +} + +float IOKitJoystick::GetAxisScale( int axis ) +{ + return IS_AXIS_VALID ? m_axes[ axis ].sensitivity : 0.0f; +} + +void IOKitJoystick::SetAxisDeadZone( int axis, float deadZone ) +{ + if ( IS_AXIS_VALID ) + { + m_axes[ axis ].deadZone = clamp( deadZone, 0.0f, 1.0f ); + } +} + +void IOKitJoystick::SetAxisMap( int axis, EJoyAxis gameAxis ) +{ + if ( IS_AXIS_VALID ) + { + m_axes[ axis ].gameAxis = ( gameAxis > JOYAXIS_None && gameAxis < NUM_JOYAXIS ) + ? gameAxis + : JOYAXIS_None; + } +} + +void IOKitJoystick::SetAxisScale( int axis, float scale ) +{ + if ( IS_AXIS_VALID ) + { + m_axes[ axis ].sensitivity = scale; + } +} + + +bool IOKitJoystick::IsSensitivityDefault() +{ + return DEFAULT_SENSITIVITY == m_sensitivity; +} + +bool IOKitJoystick::IsAxisDeadZoneDefault( int axis ) +{ + return IS_AXIS_VALID + ? ( m_axes[ axis ].deadZone == m_axes[ axis ].defaultDeadZone ) + : true; +} + +bool IOKitJoystick::IsAxisMapDefault( int axis ) +{ + return IS_AXIS_VALID + ? ( m_axes[ axis ].gameAxis == m_axes[ axis ].defaultGameAxis ) + : true; +} + +bool IOKitJoystick::IsAxisScaleDefault( int axis ) +{ + return IS_AXIS_VALID + ? ( m_axes[ axis ].sensitivity == m_axes[ axis ].defaultSensitivity ) + : true; +} + +#undef IS_AXIS_VALID + +void IOKitJoystick::SetDefaultConfig() +{ + m_sensitivity = DEFAULT_SENSITIVITY; + + const size_t axisCount = m_axes.Size(); + + for ( size_t i = 0; i < axisCount; ++i ) + { + m_axes[i].deadZone = DEFAULT_DEADZONE; + m_axes[i].sensitivity = DEFAULT_SENSITIVITY; + m_axes[i].gameAxis = JOYAXIS_None; + } + + // Two axes? Horizontal is yaw and vertical is forward. + + if ( 2 == axisCount) + { + m_axes[0].gameAxis = JOYAXIS_Yaw; + m_axes[1].gameAxis = JOYAXIS_Forward; + } + + // Three axes? First two are movement, third is yaw. + + else if ( axisCount >= 3 ) + { + m_axes[0].gameAxis = JOYAXIS_Side; + m_axes[1].gameAxis = JOYAXIS_Forward; + m_axes[2].gameAxis = JOYAXIS_Yaw; + + // Four axes? First two are movement, last two are looking around. + + if ( axisCount >= 4 ) + { + m_axes[3].gameAxis = JOYAXIS_Pitch; +// ??? m_axes[3].sensitivity = 0.75f; + + // Five axes? Use the fifth one for moving up and down. + + if ( axisCount >= 5 ) + { + m_axes[4].gameAxis = JOYAXIS_Up; + } + } + } + + // If there is only one axis, then we make no assumptions about how + // the user might want to use it. + + // Preserve defaults for config saving. + + for ( size_t i = 0; i < axisCount; ++i ) + { + m_axes[i].defaultDeadZone = m_axes[i].deadZone; + m_axes[i].defaultSensitivity = m_axes[i].sensitivity; + m_axes[i].defaultGameAxis = m_axes[i].gameAxis; + } +} + + +FString IOKitJoystick::GetIdentifier() +{ + char identifier[ 32 ] = {0}; + + snprintf( identifier, sizeof( identifier ), "VID_%04x_PID_%04x", + IOHIDDevice_GetVendorID( m_device ), IOHIDDevice_GetProductID( m_device ) ); + + return FString( identifier ); +} + + +void IOKitJoystick::AddAxes( float axes[ NUM_JOYAXIS ] ) const +{ + for ( size_t i = 0, count = m_axes.Size(); i < count; ++i ) + { + const EJoyAxis axis = m_axes[i].gameAxis; + + if ( JOYAXIS_None == axis ) + { + continue; + } + + axes[ axis ] -= m_axes[i].value; + } +} + + +void IOKitJoystick::Update() +{ + IOHIDValueRef value = NULL; + + while ( HIDGetEvent( m_device, &value ) && NULL != value ) + { + ProcessAxis( value ) || ProcessButton( value ) || ProcessPOV( value ); + + CFRelease( value ); + } +} + + +bool IOKitJoystick::ProcessAxis( const IOHIDValueRef value ) +{ + const IOHIDElementRef element = IOHIDValueGetElement( value ); + + if ( NULL == element ) + { + return false; + } + + for ( size_t i = 0, count = m_axes.Size(); i < count; ++i ) + { + if ( element != m_axes[i].element ) + { + continue; + } + + AxisInfo& axis = m_axes[i]; + + const double scaledValue = IOHIDValueGetScaledValue( value, kIOHIDValueScaleTypeCalibrated ); + const double filteredValue = Joy_RemoveDeadZone( scaledValue, axis.deadZone, NULL ); + + axis.value = static_cast< float >( filteredValue * m_sensitivity * axis.sensitivity ); + + return true; + } + + return false; +} + +bool IOKitJoystick::ProcessButton( const IOHIDValueRef value ) +{ + const IOHIDElementRef element = IOHIDValueGetElement( value ); + + if ( NULL == element ) + { + return false; + } + + for ( size_t i = 0, count = m_buttons.Size(); i < count; ++i ) + { + if ( element != m_buttons[i] ) + { + continue; + } + + const int newButton = IOHIDValueGetIntegerValue( value ) & 1; + const int oldButton = ~newButton; + + Joy_GenerateButtonEvents( oldButton, newButton, 1, + static_cast< int >( KEY_FIRSTJOYBUTTON + i ) ); + + return true; + } + + return false; +} + +bool IOKitJoystick::ProcessPOV( const IOHIDValueRef value ) +{ + const IOHIDElementRef element = IOHIDValueGetElement( value ); + + if ( NULL == element ) + { + return false; + } + + for ( size_t i = 0, count = m_POVs.Size(); i < count; ++i ) + { + if ( element != m_POVs[i] ) + { + continue; + } + + const CFIndex direction = IOHIDValueGetIntegerValue( value ); + + // Default values is for Up/North + int oldButtons = 0; + int newButtons = 1; + int numButtons = 1; + int baseButton = KEY_JOYPOV1_UP; + + switch ( direction ) + { + case 0: // N + break; + + case 1: // NE + newButtons = 3; + numButtons = 2; + break; + + case 2: // E + baseButton = KEY_JOYPOV1_RIGHT; + break; + + case 3: // SE + newButtons = 3; + numButtons = 2; + baseButton = KEY_JOYPOV1_RIGHT; + break; + + case 4: // S + baseButton = KEY_JOYPOV1_DOWN; + break; + + case 5: // SW + newButtons = 3; + numButtons = 2; + baseButton = KEY_JOYPOV1_DOWN; + break; + + case 6: // W + baseButton = KEY_JOYPOV1_LEFT; + break; + + case 7: // NW + newButtons = 9; // UP and LEFT + numButtons = 4; + break; + + default: + // release all four directions + oldButtons = 15; + newButtons = 0; + numButtons = 4; + break; + } + + Joy_GenerateButtonEvents( oldButtons, newButtons, numButtons, + static_cast< int >( baseButton + i * 4 ) ); + } + + return false; +} + + +// --------------------------------------------------------------------------- + + +class IOKitJoystickManager +{ +public: + IOKitJoystickManager(); + ~IOKitJoystickManager(); + + void GetJoysticks( TArray< IJoystickConfig* >& joysticks ) const; + + void AddAxes( float axes[ NUM_JOYAXIS ] ) const; + + // Updates axes/buttons states + void Update(); + + // Rebuilds device list + void Rescan(); + +private: + TArray< IOKitJoystick* > m_joysticks; + + static void OnDeviceChanged( void* context, IOReturn result, void* sender, IOHIDDeviceRef device ); + + void ReleaseJoysticks(); + + void EnableCallbacks(); + void DisableCallbacks(); + +}; + + +IOKitJoystickManager::IOKitJoystickManager() +{ + Rescan(); +} + +IOKitJoystickManager::~IOKitJoystickManager() +{ + ReleaseJoysticks(); + DisableCallbacks(); + + HIDReleaseDeviceList(); +} + + +void IOKitJoystickManager::GetJoysticks( TArray< IJoystickConfig* >& joysticks ) const +{ + const size_t joystickCount = m_joysticks.Size(); + + joysticks.Resize( joystickCount ); + + for ( size_t i = 0; i < joystickCount; ++i ) + { + M_LoadJoystickConfig( m_joysticks[i] ); + + joysticks[i] = m_joysticks[i]; + } +} + +void IOKitJoystickManager::AddAxes( float axes[ NUM_JOYAXIS ] ) const +{ + for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i ) + { + m_joysticks[i]->AddAxes( axes ); + } +} + + +void IOKitJoystickManager::Update() +{ + for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i ) + { + m_joysticks[i]->Update(); + } +} + + +void IOKitJoystickManager::Rescan() +{ + ReleaseJoysticks(); + DisableCallbacks(); + + const int usageCount = 2; + + const UInt32 usagePages[ usageCount ] = + { + kHIDPage_GenericDesktop, + kHIDPage_GenericDesktop + }; + + const UInt32 usages[ usageCount ] = + { + kHIDUsage_GD_Joystick, + kHIDUsage_GD_GamePad + }; + + if ( HIDUpdateDeviceList( usagePages, usages, usageCount ) ) + { + IOHIDDeviceRef device = HIDGetFirstDevice(); + + while ( NULL != device ) + { + IOKitJoystick* joystick = new IOKitJoystick( device ); + m_joysticks.Push( joystick ); + + device = HIDGetNextDevice( device ); + } + } + else + { + Printf( "IOKitJoystickManager: Failed to build gamepad/joystick device list.\n" ); + } + + EnableCallbacks(); +} + + +void IOKitJoystickManager::OnDeviceChanged( void* context, IOReturn result, void* sender, IOHIDDeviceRef device ) +{ + event_t event; + + memset( &event, 0, sizeof( event ) ); + event.type = EV_DeviceChange; + + D_PostEvent( &event ); +} + + +void IOKitJoystickManager::ReleaseJoysticks() +{ + for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i ) + { + delete m_joysticks[i]; + } + + m_joysticks.Clear(); +} + + +void IOKitJoystickManager::EnableCallbacks() +{ + if ( NULL == gIOHIDManagerRef ) + { + return; + } + + IOHIDManagerRegisterDeviceMatchingCallback( gIOHIDManagerRef, OnDeviceChanged, this ); + IOHIDManagerRegisterDeviceRemovalCallback ( gIOHIDManagerRef, OnDeviceChanged, this ); + IOHIDManagerScheduleWithRunLoop( gIOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); +} + +void IOKitJoystickManager::DisableCallbacks() +{ + if ( NULL == gIOHIDManagerRef ) + { + return; + } + + IOHIDManagerUnscheduleFromRunLoop( gIOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); + IOHIDManagerRegisterDeviceMatchingCallback( gIOHIDManagerRef, NULL, NULL ); + IOHIDManagerRegisterDeviceRemovalCallback ( gIOHIDManagerRef, NULL, NULL ); +} + + +IOKitJoystickManager* s_joystickManager; + + +} // unnamed namespace + + +// --------------------------------------------------------------------------- + + +void I_StartupJoysticks() +{ + // HID Manager API is available on 10.5 (Darwin 9.x) or newer + + if (darwinVersion.major >= 9) + { + s_joystickManager = new IOKitJoystickManager; + } +} + +void I_ShutdownJoysticks() +{ + delete s_joystickManager; +} + +void I_GetJoysticks( TArray< IJoystickConfig* >& sticks ) +{ + if ( NULL != s_joystickManager ) + { + s_joystickManager->GetJoysticks( sticks ); + } +} + +void I_GetAxes( float axes[ NUM_JOYAXIS ] ) +{ + for ( size_t i = 0; i < NUM_JOYAXIS; ++i ) + { + axes[i] = 0.0f; + } + + if ( use_joystick && NULL != s_joystickManager ) + { + s_joystickManager->AddAxes( axes ); + } +} + +IJoystickConfig* I_UpdateDeviceList() +{ + if ( use_joystick && NULL != s_joystickManager ) + { + s_joystickManager->Rescan(); + } + + return NULL; +} + + +// --------------------------------------------------------------------------- + + +void I_ProcessJoysticks() +{ + if ( use_joystick && NULL != s_joystickManager ) + { + s_joystickManager->Update(); + } +} + +#else // prior to 10.5 + +void I_StartupJoysticks() +{ +} + +void I_ShutdownJoysticks() +{ +} + +void I_GetJoysticks(TArray& sticks) +{ + sticks.Clear(); +} + +void I_GetAxes(float axes[NUM_JOYAXIS]) +{ + for (size_t i = 0; i < NUM_JOYAXIS; ++i) + { + axes[i] = 0.0f; + } +} + +IJoystickConfig *I_UpdateDeviceList() +{ + return NULL; +} + +void I_ProcessJoysticks() +{ +} + +#endif // 10.5 or higher diff --git a/src/cocoa/i_osversion.h b/src/cocoa/i_osversion.h new file mode 100755 index 000000000..5e6ac6d20 --- /dev/null +++ b/src/cocoa/i_osversion.h @@ -0,0 +1,43 @@ +/* + ** i_osversion.h + ** + **--------------------------------------------------------------------------- + ** Copyright 2012-2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include + +struct DarwinVersion +{ + uint16_t major; + uint16_t minor; + uint16_t bugfix; +}; + +extern const DarwinVersion darwinVersion; diff --git a/src/cocoa/i_rbopts.h b/src/cocoa/i_rbopts.h new file mode 100644 index 000000000..40a9ff17a --- /dev/null +++ b/src/cocoa/i_rbopts.h @@ -0,0 +1,52 @@ +/* + ** i_rbopts.h + ** + **--------------------------------------------------------------------------- + ** Copyright 2014 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#ifndef SRC_COCOA_I_RBOPTS_H_INCLUDED +#define SRC_COCOA_I_RBOPTS_H_INCLUDED + +struct RenderBufferOptions +{ + float pixelScale; + + float shiftX; + float shiftY; + + float width; + float height; + + bool dirty; +}; + +extern RenderBufferOptions rbOpts; + +#endif // SRC_COCOA_I_RBOPTS_H_INCLUDED diff --git a/src/cocoa/i_timer.cpp b/src/cocoa/i_timer.cpp new file mode 100644 index 000000000..0699a9967 --- /dev/null +++ b/src/cocoa/i_timer.cpp @@ -0,0 +1,190 @@ + +#include +#include +#include +#include + +#include + +#include "basictypes.h" +#include "basicinlines.h" +#include "doomdef.h" +#include "i_system.h" +#include "templates.h" + + +unsigned int I_MSTime() +{ + return SDL_GetTicks(); +} + +unsigned int I_FPSTime() +{ + return SDL_GetTicks(); +} + + +bool g_isTicFrozen; + + +namespace +{ + +timespec GetNextTickTime() +{ + static const long MILLISECONDS_IN_SECOND = 1000; + static const long MICROSECONDS_IN_SECOND = 1000 * MILLISECONDS_IN_SECOND; + static const long NANOSECONDS_IN_SECOND = 1000 * MICROSECONDS_IN_SECOND; + + static timespec ts = {}; + + if (__builtin_expect((0 == ts.tv_sec), 0)) + { + timeval tv; + gettimeofday(&tv, NULL); + + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = (tv.tv_usec + MICROSECONDS_IN_SECOND / TICRATE) * MILLISECONDS_IN_SECOND; + } + else + { + ts.tv_nsec += (MICROSECONDS_IN_SECOND / TICRATE) * MILLISECONDS_IN_SECOND; + } + + if (ts.tv_nsec >= NANOSECONDS_IN_SECOND) + { + ts.tv_sec++; + ts.tv_nsec -= NANOSECONDS_IN_SECOND; + } + + return ts; +} + + +pthread_cond_t s_timerEvent; +pthread_mutex_t s_timerMutex; +pthread_t s_timerThread; + +bool s_timerInitialized; +bool s_timerExitRequested; + +uint32_t s_ticStart; +uint32_t s_timerStart; + +int s_tics; + + +void* TimerThreadFunc(void*) +{ + assert(s_timerInitialized); + assert(!s_timerExitRequested); + + while (true) + { + if (s_timerExitRequested) + { + break; + } + + const timespec timeToNextTick = GetNextTickTime(); + + pthread_mutex_lock(&s_timerMutex); + pthread_cond_timedwait(&s_timerEvent, &s_timerMutex, &timeToNextTick); + + if (!g_isTicFrozen) + { + // The following GCC/Clang intrinsic can be used instead of OS X specific function: + // __sync_add_and_fetch(&s_tics, 1); + // Although it's not supported on all platform/compiler combination, + // e.g. GCC 4.0.1 with PowerPC target architecture + + OSAtomicIncrement32(&s_tics); + } + + s_timerStart = SDL_GetTicks(); + + pthread_cond_broadcast(&s_timerEvent); + pthread_mutex_unlock(&s_timerMutex); + } + + return NULL; +} + +int GetTimeThreaded(bool saveMS) +{ + if (saveMS) + { + s_ticStart = s_timerStart; + } + + return s_tics; +} + +int WaitForTicThreaded(int prevTic) +{ + assert(!g_isTicFrozen); + + while (s_tics <= prevTic) + { + pthread_mutex_lock(&s_timerMutex); + pthread_cond_wait(&s_timerEvent, &s_timerMutex); + pthread_mutex_unlock(&s_timerMutex); + } + + return s_tics; +} + +void FreezeTimeThreaded(bool frozen) +{ + g_isTicFrozen = frozen; +} + +} // unnamed namespace + + +fixed_t I_GetTimeFrac(uint32* ms) +{ + const uint32_t now = SDL_GetTicks(); + + if (NULL != ms) + { + *ms = s_ticStart + 1000 / TICRATE; + } + + return 0 == s_ticStart + ? FRACUNIT + : clamp( (now - s_ticStart) * FRACUNIT * TICRATE / 1000, 0, FRACUNIT); +} + + +void I_InitTimer () +{ + assert(!s_timerInitialized); + s_timerInitialized = true; + + pthread_cond_init (&s_timerEvent, NULL); + pthread_mutex_init(&s_timerMutex, NULL); + + pthread_create(&s_timerThread, NULL, TimerThreadFunc, NULL); + + I_GetTime = GetTimeThreaded; + I_WaitForTic = WaitForTicThreaded; + I_FreezeTime = FreezeTimeThreaded; +} + +void I_ShutdownTimer () +{ + if (!s_timerInitialized) + { + // This might happen if Cancel button was pressed + // in the IWAD selector window + return; + } + + s_timerExitRequested = true; + + pthread_join(s_timerThread, NULL); + + pthread_mutex_destroy(&s_timerMutex); + pthread_cond_destroy (&s_timerEvent); +} diff --git a/src/cocoa/zdoom-info.plist b/src/cocoa/zdoom-info.plist new file mode 100644 index 000000000..2a1911cdf --- /dev/null +++ b/src/cocoa/zdoom-info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleIconFile + zdoom.icns + CFBundleIdentifier + org.zdoom.zdoom + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ZDoom + CFBundlePackageType + APPL + CFBundleShortVersionString + Version 2.8.0 + CFBundleSignature + ???? + LSApplicationCategoryType + public.app-category.action-games + LSMinimumSystemVersion + 10.4 + CFBundleDocumentTypes + + + CFBundleTypeName + Doom Resource File + CFBundleTypeRole + Viewer + CFBundleTypeExtensions + + wad + pk3 + zip + pk7 + 7z + + + + NSPrincipalClass + NSApplication + + diff --git a/src/cocoa/zdoom.icns b/src/cocoa/zdoom.icns new file mode 100644 index 000000000..decb4c5ab Binary files /dev/null and b/src/cocoa/zdoom.icns differ diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index 697d72c58..c0b73d3c0 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -145,12 +145,20 @@ bool CT_Responder (event_t *ev) CT_BackSpace (); return true; } +#ifdef __APPLE__ + else if (ev->data1 == 'C' && (ev->data3 & GKM_META)) +#else // !__APPLE__ else if (ev->data1 == 'C' && (ev->data3 & GKM_CTRL)) +#endif // __APPLE__ { I_PutInClipboard ((char *)ChatQueue); return true; } +#ifdef __APPLE__ + else if (ev->data1 == 'V' && (ev->data3 & GKM_META)) +#else // !__APPLE__ else if (ev->data1 == 'V' && (ev->data3 & GKM_CTRL)) +#endif // __APPLE__ { CT_PasteChat(I_GetFromClipboard(false)); } diff --git a/src/d_gui.h b/src/d_gui.h index b88041771..86c3761ed 100644 --- a/src/d_gui.h +++ b/src/d_gui.h @@ -70,7 +70,8 @@ enum GUIKeyModifiers GKM_SHIFT = 1, GKM_CTRL = 2, GKM_ALT = 4, - GKM_LBUTTON = 8 + GKM_META = 8, + GKM_LBUTTON = 16 }; // Special codes for some GUI keys, including a few real ASCII codes. diff --git a/src/g_game.cpp b/src/g_game.cpp index e419e09fc..734500e55 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1128,11 +1128,8 @@ void G_Ticker () // check, not just the player's x position like BOOM. DWORD rngsum = FRandom::StaticSumSeeds (); - if ((gametic % ticdup) == 0) - { - //Added by MC: For some of that bot stuff. The main bot function. - bglobal.Main (buf); - } + //Added by MC: For some of that bot stuff. The main bot function. + bglobal.Main (); for (i = 0; i < MAXPLAYERS; i++) { @@ -1395,7 +1392,6 @@ void G_PlayerReborn (int player) if (gamestate != GS_TITLELEVEL) { - // [GRB] Give inventory specified in DECORATE actor->GiveDefaultInventory (); p->ReadyWeapon = p->PendingWeapon; @@ -1406,6 +1402,7 @@ void G_PlayerReborn (int player) { botskill_t skill = p->Bot->skill; p->Bot->Clear (); + p->Bot->player = p; p->Bot->skill = skill; } } diff --git a/src/g_level.cpp b/src/g_level.cpp index 7eeb443fb..bff8b9171 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1164,6 +1164,8 @@ void G_StartTravel () } } } + + bglobal.StartTravel (); } //========================================================================== @@ -1261,6 +1263,8 @@ void G_FinishTravel () } } } + + bglobal.FinishTravel (); } //========================================================================== diff --git a/src/g_level.h b/src/g_level.h index 05290f48b..8e78361d2 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -52,7 +52,7 @@ class FScanner; #define GCC_YSEG #else #define MSVC_YSEG -#define GCC_YSEG __attribute__((section(SECTION_YREG))) +#define GCC_YSEG __attribute__((section(SECTION_YREG))) __attribute__((used)) #endif struct FIntermissionDescriptor; diff --git a/src/g_shared/a_armor.cpp b/src/g_shared/a_armor.cpp index a745197c2..0f343e523 100644 --- a/src/g_shared/a_armor.cpp +++ b/src/g_shared/a_armor.cpp @@ -516,7 +516,14 @@ void AHexenArmor::AbsorbDamage (int damage, FName damageType, int &newdamage) // with the dragon skin bracers. if (damage < 10000) { +#if __APPLE__ && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 1 + // -O1 optimizer bug work around. Only needed for + // GCC 4.2.1 on OS X for 10.4/10.5 tools compatibility. + volatile fixed_t tmp = 300; + Slots[i] -= Scale (damage, SlotsIncrement[i], tmp); +#else Slots[i] -= Scale (damage, SlotsIncrement[i], 300); +#endif if (Slots[i] < 2*FRACUNIT) { Slots[i] = 0; diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index fc3e863a2..1aab998a9 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1024,7 +1024,7 @@ void AInventory::Touch (AActor *toucher) //Added by MC: Check if item taken was the roam destination of any bot for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].Bot != NULL && this == players[i].Bot->dest) + if (players[i].Bot != NULL && this == players[i].Bot->dest) players[i].Bot->dest = NULL; } } diff --git a/src/gccinlines.h b/src/gccinlines.h index b905449de..ecdf45df6 100644 --- a/src/gccinlines.h +++ b/src/gccinlines.h @@ -41,7 +41,7 @@ static inline SDWORD Scale (SDWORD a, SDWORD b, SDWORD c) : "a,a,a,a,a,a" (a), "m,r,m,r,d,d" (b), "r,r,m,m,r,m" (c) - : "%cc" + : "cc" ); return result; @@ -59,7 +59,7 @@ static inline SDWORD MulScale (SDWORD a, SDWORD b, SDWORD c) : "a,a,a,a" (a), "m,r,m,r" (b), "c,c,I,I" (c) - : "%cc" + : "cc" ); return result; } @@ -210,7 +210,7 @@ static inline SDWORD DivScale (SDWORD a, SDWORD b, SDWORD c) : "a" (lo), "d" (hi), "r" (b) - : "%cc"); + : "cc"); return result; } @@ -226,7 +226,7 @@ static inline SDWORD DivScale1 (SDWORD a, SDWORD b) "=&d,d" (dummy) : "a,a" (a), "r,m" (b) - : "%cc"); + : "cc"); return result; } @@ -241,7 +241,7 @@ static inline SDWORD DivScale1 (SDWORD a, SDWORD b) : "a,a" (a<>(32-s)), \ "r,m" (b) \ - : "%cc"); \ + : "cc"); \ return result; \ } @@ -287,7 +287,7 @@ static inline SDWORD DivScale32 (SDWORD a, SDWORD b) "=d,d" (dummy) : "d,d" (a), "r,m" (b) - : "%cc"); + : "cc"); return result; } @@ -313,7 +313,7 @@ static inline void clearbufshort (void *buff, unsigned int count, WORD clear) "rep stosw" :"=D" (buff), "=c" (count) :"D" (buff), "c" (count), "a" (clear|(clear<<16)) - :"%cc"); + :"cc"); } static inline SDWORD ksgn (SDWORD a) @@ -327,6 +327,6 @@ static inline SDWORD ksgn (SDWORD a) "adc $0,%1" :"=r" (dummy), "=r" (result) :"0" (a) - :"%cc"); + :"cc"); return result; } diff --git a/src/nodebuild_utility.cpp b/src/nodebuild_utility.cpp index e7d5865d5..14ab7be58 100644 --- a/src/nodebuild_utility.cpp +++ b/src/nodebuild_utility.cpp @@ -69,7 +69,13 @@ static const int PO_LINE_EXPLICIT = 5; angle_t FNodeBuilder::PointToAngle (fixed_t x, fixed_t y) { const double rad2bam = double(1<<30) / M_PI; +#if defined __APPLE__ && !defined __llvm__ + // Work-around for vectorization issue in Apple's GCC 4.x + // See https://gcc.gnu.org/wiki/Math_Optimization_Flags for details + long double ang = atan2l (double(y), double(x)); +#else // !__APPLE__ || __llvm__ double ang = atan2 (double(y), double(x)); +#endif // __APPLE__ && !__llvm__ return angle_t(ang * rad2bam) << 1; } diff --git a/src/oplsynth/mlopl_io.cpp b/src/oplsynth/mlopl_io.cpp index b9d06629f..691463470 100644 --- a/src/oplsynth/mlopl_io.cpp +++ b/src/oplsynth/mlopl_io.cpp @@ -323,7 +323,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3) { assert(numchips >= 1 && numchips <= countof(chips)); uint i; - IsOPL3 = (opl_core == 1 || opl_core == 2); + IsOPL3 = (opl_core == 1 || opl_core == 2 || opl_core == 3); memset(chips, 0, sizeof(chips)); if (IsOPL3) @@ -332,7 +332,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3) } for (i = 0; i < numchips; ++i) { - OPLEmul *chip = IsOPL3 ? (opl_core == 1 ? DBOPLCreate(stereo) : JavaOPLCreate(stereo)) : YM3812Create(stereo); + OPLEmul *chip = IsOPL3 ? (opl_core == 1 ? DBOPLCreate(stereo) : (opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); if (chip == NULL) { break; diff --git a/src/oplsynth/nukedopl3.cpp b/src/oplsynth/nukedopl3.cpp new file mode 100644 index 000000000..90673d855 --- /dev/null +++ b/src/oplsynth/nukedopl3.cpp @@ -0,0 +1,1147 @@ +/* +* Copyright (C) 2013-2014 Nuke.YKT +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Nuked Yamaha YMF262(aka OPL3) emulator. + Thanks: + MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + Feedback and Rhythm part calculation information. + forums.submarine.org.uk(carbon14, opl3): + Tremolo and phase generator calculation information. + OPLx decapsulated(Matthew Gambrell and Olli Niemitalo): + OPL2 ROMs. +*/ + +//version 1.4.2 + +/* Changelog: + v1.1: + Vibrato's sign fix + v1.2: + Operator key fix + Corrected 4-operator mode + Corrected rhythm mode + Some small fixes + v1.2.1: + Small envelope generator fix + Removed EX_Get function(not used) + v1.3: + Complete rewrite + (Not released) + v1.4: + New envelope and waveform generator + Some small fixes. + (Not released) + v1.4.1: + Envelope generator rate calculation fix + (Not released) + v1.4.2: + Version for ZDoom. +*/ + + +/* Verified: + Noise generator. + Waveform generator. + Envelope generator increase table. + Tremolo. +*/ + +/* TODO: + Verify: + kslrom[15] value(is it 128?). + Sustain level = 15. + Vibrato, Phase generator. + Rhythm part. + Envelope generator state switching(decay->sustain when egt = 1 and decay->release). + Feedback. + Register write. + 4-operator. +*/ + +#include +#include +#include "nukedopl3.h" + +// Channel types + +enum { + ch_4op2, + ch_2op, + ch_4op, + ch_drum +}; + +// Envelope generator states + +enum { + eg_off, + eg_attack, + eg_decay, + eg_sustain, + eg_release +}; + +// Envelope key types + +enum { + egk_norm = 1, + egk_drum = 2 +}; + +// +// logsin table +// + +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +// +// exp table +// + +static const Bit16u exprom[256] = { + 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, + 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, + 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, + 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, + 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, + 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, + 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, + 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, + 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, + 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, + 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, + 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, + 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, + 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +}; + +// +// freq mult table multiplied by 2 +// +// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 +// + +static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; + +// +// ksl table +// + +static const Bit8u kslrom[16] = { 0, 64, 80, 90, 96, 102, 106, 110, 112, 116, 118, 120, 122, 124, 126, 127 }; + +static const Bit8u kslshift[4] = { 8, 1, 2, 0 }; + +// +// LFO vibrato +// + +static const Bit8u vib_table[8] = { 3, 1, 0, 1, 3, 1, 0, 1 }; +static const Bit8s vibsgn_table[8] = { 1, 1, 1, 1, -1, -1, -1, -1 }; + +// +// envelope generator constants +// + +static const Bit8u eg_incstep[3][4][8] = { + { { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } }, + { { 0, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 1, 1, 0, 1 }, { 1, 1, 1, 1, 1, 1, 0, 1 } }, + { { 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 2, 2, 1, 1 }, { 2, 2, 2, 2, 2, 2, 1, 1 } } +}; + +// +// address decoding +// + +static const Bit8s ad_slot[0x20] = { 0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1, 12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +static const Bit8u op_offset[18] = { 0x00, 0x03, 0x01, 0x04, 0x02, 0x05, 0x08, 0x0b, 0x09, 0x0c, 0x0a, 0x0d, 0x10, 0x13, 0x11, 0x14, 0x12, 0x15 }; + + + +static const Bit8u eg_incdesc[16] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 +}; + +static const Bit8s eg_incsh[16] = { + 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2 +}; + +typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); +typedef void(*envelope_genfunc)(slot *slott); + +// +// Phase generator +// + +void PG_Generate(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + channel *chan = &opl->Channels[op / 2]; + Bit16u fnum = chan->f_number; + if (slt->vibrato) { + Bit8u fnum_high = chan->f_number >> (7 + vib_table[opl->vib_pos] + (!opl->dvb)); + fnum += fnum_high * vibsgn_table[opl->vib_pos]; + } + slt->PG_pos += (((fnum << chan->block) >> 1) * mt[slt->mult]) >> 1; +} + +// +// Envelope generator +// + +Bit16s envelope_calcexp(Bit32u level) { + return ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 1) >> (level >> 8); +} + +Bit16s envelope_calcsin0(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + Bit16u neg = 0; + if (phase & 0x200 && (phase & 0x1ff)) { + phase--; + neg = ~0; + } + if (phase & 0x100) { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)) ^ neg; +} + +Bit16s envelope_calcsin1(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x200) { + out = 0x1000; + } + else if (phase & 0x100) { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin2(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x100) { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin3(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x100) { + out = 0x1000; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin4(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + Bit16u neg = 0; + if ((phase & 0x300) == 0x100 && (phase & 0xff)) { + phase--; + neg = ~0; + } + if (phase & 0x200) { + out = 0x1000; + } + else if (phase & 0x80) { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else { + out = logsinrom[(phase << 1) & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)) ^ neg; +} + +Bit16s envelope_calcsin5(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x200) { + out = 0x1000; + } + else if (phase & 0x80) { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else { + out = logsinrom[(phase << 1) & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin6(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u neg = 0; + if (phase & 0x200 && (phase & 0x1ff)) { + phase--; + neg = ~0; + } + return envelope_calcexp(envelope << 3) ^ neg; +} + +Bit16s envelope_calcsin7(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + Bit16u neg = 0; + if (phase & 0x200 && (phase & 0x1ff)) { + phase--; + neg = ~0; + phase = (phase & 0x1ff) ^ 0x1ff; + } + out = phase << 3; + return envelope_calcexp(out + (envelope << 3)) ^ neg; +} + +envelope_sinfunc envelope_sin[8] = { + envelope_calcsin0, + envelope_calcsin1, + envelope_calcsin2, + envelope_calcsin3, + envelope_calcsin4, + envelope_calcsin5, + envelope_calcsin6, + envelope_calcsin7 +}; + +void envelope_gen_off(slot *slott); +void envelope_gen_change(slot *slott); +void envelope_gen_attack(slot *slott); +void envelope_gen_decay(slot *slott); +void envelope_gen_sustain(slot *slott); +void envelope_gen_release(slot *slott); + +envelope_genfunc envelope_gen[6] = { + envelope_gen_off, + envelope_gen_attack, + envelope_gen_decay, + envelope_gen_sustain, + envelope_gen_release, + envelope_gen_change +}; + +enum envelope_gen_num { + envelope_gen_num_off = 0, + envelope_gen_num_attack = 1, + envelope_gen_num_decay = 2, + envelope_gen_num_sustain = 3, + envelope_gen_num_release = 4, + envelope_gen_num_change = 5 +}; + +void envelope_gen_off(slot *slott) { + slott->EG_out = 0x1ff; +} + +void envelope_gen_change(slot *slott) { + slott->eg_gen = slott->eg_gennext; +} + +void envelope_gen_attack(slot *slott) { + slott->EG_out += ((~slott->EG_out) *slott->eg_inc) >> 3; + if (slott->EG_out < 0x00) { + slott->EG_out = 0x00; + } + if (slott->EG_out == 0x00) { + slott->eg_gen = envelope_gen_num_change; + slott->eg_gennext = envelope_gen_num_decay; + } +} + +void envelope_gen_decay(slot *slott) { + slott->EG_out += slott->eg_inc; + if (slott->EG_out >= slott->EG_sl << 4) { + slott->eg_gen = envelope_gen_num_change; + slott->eg_gennext = envelope_gen_num_sustain; + } +} + +void envelope_gen_sustain(slot *slott) { + if (!slott->EG_type) { + envelope_gen_release(slott); + } +} + +void envelope_gen_release(slot *slott) { + slott->EG_out += slott->eg_inc; + if (slott->EG_out >= 0x1ff) { + slott->eg_gen = envelope_gen_num_change; + slott->eg_gennext = envelope_gen_num_off; + } +} + +Bit8u EG_CalcRate(chip *opl, Bit8u op, Bit8u rate) { + slot *slt = &opl->OPs[op]; + channel *chan = &opl->Channels[op / 2]; + if (rate == 0x00) { + return 0x00; + } + Bit8u rof = slt->ksr ? chan->ksv : (chan->ksv >> 2); + Bit8u rat = (rate << 2) + rof; + if (rat > 0x3c) { + rat = 0x3c; + } + return rat; +} + +void envelope_calc(chip *opl, Bit8u op) { + slot *slott = &opl->OPs[op]; + Bit16u timer = opl->timer; + Bit8u rate_h, rate_l; + Bit8u rate; + Bit8u reg_rate = 0;; + switch (slott->eg_gen) { + case envelope_gen_num_attack: + reg_rate = slott->EG_ar; + break; + case envelope_gen_num_decay: + reg_rate = slott->EG_dr; + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + reg_rate = slott->EG_rr; + break; + } + rate = EG_CalcRate(opl, op, reg_rate); + rate_h = rate >> 2; + rate_l = rate & 3; + Bit8u inc = 0; + if (slott->eg_gen == envelope_gen_num_attack && rate_h == 0x0f) { + inc = 8; + } + else if (eg_incsh[rate_h] > 0) { + if ((timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l][((timer) >> eg_incsh[rate_h]) & 0x07]; + } + } + else { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l][timer & 0x07] << (-eg_incsh[rate_h]); + } + slott->eg_inc = inc; + envelope_gen[slott->eg_gen](slott); +} + +void EG_UpdateKSL(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + channel *chan = &opl->Channels[op / 2]; + Bit8u fnum_high = (chan->f_number >> 6) & 0x0f; + Bit16s ksl = (kslrom[fnum_high] << 1) - ((chan->block ^ 0x07) << 5) - 0x20; + if (ksl < 0x00) { + ksl = 0x00; + } + slt->EG_ksl = ksl >> kslshift[slt->ksl]; +} + +void EG_Generate(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + envelope_calc(opl, op); + slt->EG_mout = slt->EG_out + slt->EG_ksl + (slt->EG_tl << 2); + if (slt->tremolo) { + slt->EG_mout += opl->trem_val; + } + if (slt->EG_mout > 0x1ff) { + slt->EG_mout = 0x1ff; + } +} + +void EG_KeyOn(chip *opl, Bit8u op, Bit8u type) { + slot *slt = &opl->OPs[op]; + if (!slt->key) { + slt->EG_state = eg_attack; + slt->eg_gen = envelope_gen_num_change; + slt->eg_gennext = envelope_gen_num_attack; + slt->PG_pos = 0; + } + slt->key |= type; +} + +void EG_KeyOff(chip *opl, Bit8u op, Bit8u type) { + slot *slt = &opl->OPs[op]; + if (slt->key) { + slt->key &= (~type); + if (slt->key == 0x00) { + slt->EG_state = eg_release; + slt->eg_gen = envelope_gen_num_change; + slt->eg_gennext = envelope_gen_num_release; + } + } +} + +// +// Noise Generator +// + +void N_Generate(chip *opl) { + if (opl->noise & 1) { + opl->noise ^= 0x800302; + } + opl->noise >>= 1; +} + +// +// Operator(Slot) +// + +void OP_Update20(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->tremolo = (opl->opl_memory[0x20 + slt->offset] >> 7); + slt->vibrato = (opl->opl_memory[0x20 + slt->offset] >> 6) & 0x01; + slt->EG_type = (opl->opl_memory[0x20 + slt->offset] >> 5) & 0x01; + slt->ksr = (opl->opl_memory[0x20 + slt->offset] >> 4) & 0x01; + slt->mult = (opl->opl_memory[0x20 + slt->offset]) & 0x0f; +} + +void OP_Update40(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->EG_tl = (opl->opl_memory[0x40 + slt->offset]) & 0x3f; + slt->ksl = (opl->opl_memory[0x40 + slt->offset] >> 6) & 0x03; + EG_UpdateKSL(opl, op); +} + +void OP_Update60(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->EG_dr = (opl->opl_memory[0x60 + slt->offset]) & 0x0f; + slt->EG_ar = (opl->opl_memory[0x60 + slt->offset] >> 4) & 0x0f; +} + +void OP_Update80(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->EG_rr = (opl->opl_memory[0x80 + slt->offset]) & 0x0f; + slt->EG_sl = (opl->opl_memory[0x80 + slt->offset] >> 4) & 0x0f; + if (slt->EG_sl == 0x0f) { + slt->EG_sl = 0x1f; + } +} + +void OP_UpdateE0(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->waveform = opl->opl_memory[0xe0 + slt->offset] & 0x07; + if (!opl->newm) { + slt->waveform &= 0x03; + } +} + +void OP_GeneratePhase(chip *opl, Bit8u op, Bit16u phase) { + slot *slt = &opl->OPs[op]; + slt->out = envelope_sin[slt->waveform](phase, slt->EG_out); +} + +void OP_Generate(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->out = envelope_sin[slt->waveform]((Bit16u)((slt->PG_pos >> 9) + (*slt->mod)), slt->EG_mout); +} + +void OP_GenerateZM(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + slt->out = envelope_sin[slt->waveform]((Bit16u)(slt->PG_pos >> 9), slt->EG_mout); +} + +void OP_CalcFB(chip *opl, Bit8u op) { + slot *slt = &opl->OPs[op]; + channel *chan = &opl->Channels[op / 2]; + slt->prevout[1] = slt->prevout[0]; + slt->prevout[0] = slt->out; + if (chan->feedback) { + slt->fbmod = (slt->prevout[0] + slt->prevout[1]) >> chan->feedback; + } else { + slt->fbmod = 0; + } +} + +// +// Channel +// + +void CH_UpdateRhythm(chip *opl) { + opl->rhythm = (opl->opl_memory[0xbd] & 0x3f); + if (opl->rhythm & 0x20) { + for (Bit8u i = 6; i < 9; i++) { + opl->Channels[i].chtype = ch_drum; + } + //HH + if (opl->rhythm & 0x01) { + EG_KeyOn(opl, 14, egk_drum); + } else { + EG_KeyOff(opl, 14, egk_drum); + } + //TC + if (opl->rhythm & 0x02) { + EG_KeyOn(opl, 17, egk_drum); + } else { + EG_KeyOff(opl, 17, egk_drum); + } + //TOM + if (opl->rhythm & 0x04) { + EG_KeyOn(opl, 16, egk_drum); + } else { + EG_KeyOff(opl, 16, egk_drum); + } + //SD + if (opl->rhythm & 0x08) { + EG_KeyOn(opl, 15, egk_drum); + } else { + EG_KeyOff(opl, 15, egk_drum); + } + //BD + if (opl->rhythm & 0x10) { + EG_KeyOn(opl, 12, egk_drum); + EG_KeyOn(opl, 13, egk_drum); + } else { + EG_KeyOff(opl, 12, egk_drum); + EG_KeyOff(opl, 13, egk_drum); + } + } else { + for (Bit8u i = 6; i < 9; i++) { + opl->Channels[i].chtype = ch_2op; + } + } +} + +void CH_UpdateAB0(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + if (opl->newm && chan->chtype == ch_4op2) { + return; + } + Bit16u f_number = (opl->opl_memory[0xa0 + chan->offset]) | (((opl->opl_memory[0xb0 + chan->offset]) & 0x03) << 8); + Bit8u block = ((opl->opl_memory[0xb0 + chan->offset]) >> 2) & 0x07; + Bit8u ksv = block * 2 | ((f_number >> (9 - opl->nts)) & 0x01); + chan->f_number = f_number; + chan->block = block; + chan->ksv = ksv; + EG_UpdateKSL(opl, ch * 2); + EG_UpdateKSL(opl, ch * 2 + 1); + OP_Update60(opl, ch * 2); + OP_Update60(opl, ch * 2 + 1); + OP_Update80(opl, ch * 2); + OP_Update80(opl, ch * 2 + 1); + if (opl->newm && chan->chtype == ch_4op) { + chan = &opl->Channels[ch + 3]; + chan->f_number = f_number; + chan->block = block; + chan->ksv = ksv; + EG_UpdateKSL(opl, (ch + 3) * 2); + EG_UpdateKSL(opl, (ch + 3) * 2 + 1); + OP_Update60(opl, (ch + 3) * 2); + OP_Update60(opl, (ch + 3) * 2 + 1); + OP_Update80(opl, (ch + 3) * 2); + OP_Update80(opl, (ch + 3) * 2 + 1); + } +} + +void CH_SetupAlg(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + if (chan->alg & 0x08) { + return; + } + if (chan->alg & 0x04) { + switch (chan->alg & 0x03) { + case 0: + opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; + opl->OPs[(ch - 3) * 2 + 1].mod = &opl->OPs[(ch - 3) * 2].out; + opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; + opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; + break; + case 1: + opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; + opl->OPs[(ch - 3) * 2 + 1].mod = &opl->OPs[(ch - 3) * 2].out; + opl->OPs[ch * 2].mod = &opl->zm; + opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; + break; + case 2: + opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; + opl->OPs[(ch - 3) * 2 + 1].mod = &opl->zm; + opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; + opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; + break; + case 3: + opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; + opl->OPs[(ch - 3) * 2 + 1].mod = &opl->zm; + opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; + opl->OPs[ch * 2 + 1].mod = &opl->zm; + break; + } + } else { + switch (chan->alg & 0x01) { + case 0: + opl->OPs[ch * 2].mod = &opl->OPs[ch * 2].fbmod; + opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; + break; + case 1: + opl->OPs[ch * 2].mod = &opl->OPs[ch * 2].fbmod; + opl->OPs[ch * 2 + 1].mod = &opl->zm; + break; + } + } +} + +void CH_UpdateC0(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + Bit8u fb = (opl->opl_memory[0xc0 + chan->offset] & 0x0e) >> 1; + chan->feedback = fb ? (9 - fb) : 0; + chan->con = opl->opl_memory[0xc0 + chan->offset] & 0x01; + chan->alg = chan->con; + if (opl->newm) { + if (chan->chtype == ch_4op) { + channel *chan1 = &opl->Channels[ch + 3]; + chan1->alg = 0x04 | (chan->con << 1) | (chan1->con); + chan->alg = 0x08; + CH_SetupAlg(opl, ch + 3); + } else if (chan->chtype == ch_4op2) { + channel *chan1 = &opl->Channels[ch - 3]; + chan->alg = 0x04 | (chan1->con << 1) | (chan->con); + chan1->alg = 0x08; + CH_SetupAlg(opl, ch); + } else { + CH_SetupAlg(opl, ch); + } + } else { + CH_SetupAlg(opl, ch); + } + if (opl->newm) { + chan->cha = ((opl->opl_memory[0xc0 + chan->offset] >> 4) & 0x01) ? ~0 : 0; + chan->chb = ((opl->opl_memory[0xc0 + chan->offset] >> 5) & 0x01) ? ~0 : 0; + chan->chc = ((opl->opl_memory[0xc0 + chan->offset] >> 6) & 0x01) ? ~0 : 0; + chan->chd = ((opl->opl_memory[0xc0 + chan->offset] >> 7) & 0x01) ? ~0 : 0; + } else { + opl->Channels[ch].cha = opl->Channels[ch].chb = ~0; + opl->Channels[ch].chc = opl->Channels[ch].chd = 0; + } +} + +void CH_Set2OP(chip *opl) { + for (Bit8u i = 0; i < 18; i++) { + opl->Channels[i].chtype = ch_2op; + CH_UpdateC0(opl, i); + } +} + +void CH_Set4OP(chip *opl) { + for (Bit8u i = 0; i < 3; i++) { + if ((opl->opl_memory[0x104] >> i) & 0x01) { + opl->Channels[i].chtype = ch_4op; + opl->Channels[i + 3].chtype = ch_4op2; + CH_UpdateC0(opl, i); + CH_UpdateC0(opl, i + 3); + } + if ((opl->opl_memory[0x104] >> (i + 3)) & 0x01) { + opl->Channels[i + 9].chtype = ch_4op; + opl->Channels[i + 3 + 9].chtype = ch_4op2; + CH_UpdateC0(opl, i + 9); + CH_UpdateC0(opl, i + 3 + 9); + } + } +} + +void CH_GenerateRhythm(chip *opl) { + if (opl->rhythm & 0x20) { + channel *chan6 = &opl->Channels[6]; + channel *chan7 = &opl->Channels[7]; + channel *chan8 = &opl->Channels[8]; + slot *slt12 = &opl->OPs[12]; + slot *slt13 = &opl->OPs[13]; + slot *slt14 = &opl->OPs[14]; + slot *slt15 = &opl->OPs[15]; + slot *slt16 = &opl->OPs[16]; + slot *slt17 = &opl->OPs[17]; + //BD + OP_Generate(opl, 12); + OP_Generate(opl, 13); + chan6->out = slt13->out * 2; + Bit16u P14 = (slt14->PG_pos >> 9) & 0x3ff; + Bit16u P17 = (slt17->PG_pos >> 9) & 0x3ff; + Bit16u phase = 0; + // HH TC Phase bit + Bit16u PB = ((P14 & 0x08) | (((P14 >> 5) ^ P14) & 0x04) | (((P17 >> 2) ^ P17) & 0x08)) ? 0x01 : 0x00; + //HH + phase = (PB << 9) | (0x34 << ((PB ^ (opl->noise & 0x01) << 1))); + OP_GeneratePhase(opl, 14, phase); + //SD + phase = (0x100 << ((P14 >> 8) & 0x01)) ^ ((opl->noise & 0x01) << 8); + OP_GeneratePhase(opl, 15, phase); + //TT + OP_GenerateZM(opl, 16); + //TC + phase = 0x100 | (PB << 9); + OP_GeneratePhase(opl, 17, phase); + chan7->out = (slt14->out + slt15->out) * 2; + chan8->out = (slt16->out + slt17->out) * 2; + } +} + +void CH_Generate(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + if (chan->chtype == ch_drum) { + return; + } + if (chan->alg & 0x08) { + chan->out = 0; + return; + } else if (chan->alg & 0x04) { + OP_Generate(opl, (ch - 3) * 2); + OP_Generate(opl, (ch - 3) * 2 + 1); + OP_Generate(opl, ch * 2); + OP_Generate(opl, ch * 2 + 1); + switch (chan->alg & 0x03) { + case 0: + chan->out = opl->OPs[ch * 2 + 1].out; + break; + case 1: + chan->out = opl->OPs[(ch - 3) * 2 + 1].out + opl->OPs[ch * 2 + 1].out; + break; + case 2: + chan->out = opl->OPs[(ch - 3) * 2].out + opl->OPs[ch * 2 + 1].out; + break; + case 3: + chan->out = opl->OPs[(ch - 3) * 2].out + opl->OPs[ch * 2].out + opl->OPs[ch * 2 + 1].out; + break; + } + } + else { + OP_Generate(opl, ch * 2); + OP_Generate(opl, ch * 2 + 1); + switch (chan->alg & 0x01) { + case 0: + chan->out = opl->OPs[ch * 2 + 1].out; + break; + case 1: + chan->out = opl->OPs[ch * 2].out + opl->OPs[ch * 2 + 1].out; + break; + } + } +} + +void CH_Enable(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + if (opl->newm) { + if (chan->chtype == ch_4op) { + EG_KeyOn(opl, ch * 2, egk_norm); + EG_KeyOn(opl, ch * 2 + 1, egk_norm); + EG_KeyOn(opl, (ch + 3) * 2, egk_norm); + EG_KeyOn(opl, (ch + 3) * 2 + 1, egk_norm); + } + else if (chan->chtype == ch_2op || chan->chtype == ch_drum) { + EG_KeyOn(opl, ch * 2, egk_norm); + EG_KeyOn(opl, ch * 2 + 1, egk_norm); + } + } + else { + EG_KeyOn(opl, ch * 2, egk_norm); + EG_KeyOn(opl, ch * 2 + 1, egk_norm); + } +} + +void CH_Disable(chip *opl, Bit8u ch) { + channel *chan = &opl->Channels[ch]; + if (opl->newm) { + if (chan->chtype == ch_4op) { + EG_KeyOff(opl, ch * 2, egk_norm); + EG_KeyOff(opl, ch * 2 + 1, egk_norm); + EG_KeyOff(opl, (ch + 3) * 2, egk_norm); + EG_KeyOff(opl, (ch + 3) * 2 + 1, egk_norm); + } + else if (chan->chtype == ch_2op || chan->chtype == ch_drum) { + EG_KeyOff(opl, ch * 2, egk_norm); + EG_KeyOff(opl, ch * 2 + 1, egk_norm); + } + } + else { + EG_KeyOff(opl, ch * 2, egk_norm); + EG_KeyOff(opl, ch * 2 + 1, egk_norm); + } +} + +Bit16s limshort(Bit32s a) { + if (a > 32767) { + a = 32767; + } + else if (a < -32768) { + a = -32768; + } + return (Bit16s)a; +} + +void NukedOPL3::Reset() { + for (Bit8u i = 0; i < 36; i++) { + opl3.OPs[i].PG_pos = 0; + opl3.OPs[i].PG_inc = 0; + opl3.OPs[i].EG_out = 0x1ff; + opl3.OPs[i].EG_mout = 0x1ff; + opl3.OPs[i].eg_inc = 0; + opl3.OPs[i].eg_gen = 0; + opl3.OPs[i].eg_gennext = 0; + opl3.OPs[i].EG_ksl = 0; + opl3.OPs[i].EG_ar = 0; + opl3.OPs[i].EG_dr = 0; + opl3.OPs[i].EG_sl = 0; + opl3.OPs[i].EG_rr = 0; + opl3.OPs[i].EG_state = eg_off; + opl3.OPs[i].EG_type = 0; + opl3.OPs[i].out = 0; + opl3.OPs[i].prevout[0] = 0; + opl3.OPs[i].prevout[1] = 0; + opl3.OPs[i].fbmod = 0; + opl3.OPs[i].offset = op_offset[i % 18] + ((i > 17) << 8); + opl3.OPs[i].mult = 0; + opl3.OPs[i].vibrato = 0; + opl3.OPs[i].tremolo = 0; + opl3.OPs[i].ksr = 0; + opl3.OPs[i].EG_tl = 0; + opl3.OPs[i].ksl = 0; + opl3.OPs[i].key = 0; + opl3.OPs[i].waveform = 0; + } + for (Bit8u i = 0; i < 9; i++) { + opl3.Channels[i].con = 0; + opl3.Channels[i + 9].con = 0; + opl3.Channels[i].chtype = ch_2op; + opl3.Channels[i + 9].chtype = ch_2op; + opl3.Channels[i].alg = 0; + opl3.Channels[i + 9].alg = 0; + opl3.Channels[i].offset = i; + opl3.Channels[i + 9].offset = 0x100 + i; + opl3.Channels[i].feedback = 0; + opl3.Channels[i + 9].feedback = 0; + opl3.Channels[i].out = 0; + opl3.Channels[i + 9].out = 0; + opl3.Channels[i].cha = ~0; + opl3.Channels[i + 9].cha = ~0; + opl3.Channels[i].chb = ~0; + opl3.Channels[i + 9].chb = ~0; + opl3.Channels[i].chc = 0; + opl3.Channels[i + 9].chc = 0; + opl3.Channels[i].chd = 0; + opl3.Channels[i + 9].chd = 0; + opl3.Channels[i].out = 0; + opl3.Channels[i + 9].out = 0; + opl3.Channels[i].f_number = 0; + opl3.Channels[i + 9].f_number = 0; + opl3.Channels[i].block = 0; + opl3.Channels[i + 9].block = 0; + opl3.Channels[i].ksv = 0; + opl3.Channels[i + 9].ksv = 0; + opl3.Channels[i].panl = (float)CENTER_PANNING_POWER; + opl3.Channels[i + 9].panl = (float)CENTER_PANNING_POWER; + opl3.Channels[i].panr = (float)CENTER_PANNING_POWER; + opl3.Channels[i + 9].panr = (float)CENTER_PANNING_POWER; + } + memset(opl3.opl_memory, 0, 0x200); + opl3.newm = 0; + opl3.nts = 0; + opl3.rhythm = 0; + opl3.dvb = 0; + opl3.dam = 0; + opl3.noise = 0x306600; + opl3.vib_pos = 0; + opl3.timer = 0; + opl3.trem_inc = 0; + opl3.trem_tval = 0; + opl3.trem_dir = 0; + opl3.trem_val = 0; + opl3.zm = 0; + CH_Set2OP(&opl3); +} + +void NukedOPL3::WriteReg(int reg, int v) { + v &= 0xff; + reg &= 0x1ff; + Bit8u highbank = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + opl3.opl_memory[reg & 0x1ff] = v; + switch (regm & 0xf0) { + case 0x00: + if (highbank) { + switch (regm & 0x0f) { + case 0x04: + CH_Set2OP(&opl3); + CH_Set4OP(&opl3); + break; + case 0x05: + opl3.newm = v & 0x01; + break; + } + } + else { + switch (regm & 0x0f) { + case 0x08: + opl3.nts = (v >> 6) & 0x01; + break; + } + } + break; + case 0x20: + case 0x30: + if (ad_slot[regm & 0x1f] >= 0) { + OP_Update20(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + } + break; + case 0x40: + case 0x50: + if (ad_slot[regm & 0x1f] >= 0) { + OP_Update40(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + } + break; + case 0x60: + case 0x70: + if (ad_slot[regm & 0x1f] >= 0) { + OP_Update60(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + } + break; + case 0x80: + case 0x90: + if (ad_slot[regm & 0x1f] >= 0) { + OP_Update80(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + } + break; + case 0xe0: + case 0xf0: + if (ad_slot[regm & 0x1f] >= 0) { + OP_UpdateE0(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + } + break; + case 0xa0: + if ((regm & 0x0f) < 9) { + CH_UpdateAB0(&opl3, 9 * highbank + (regm & 0x0f)); + } + break; + case 0xb0: + if (regm == 0xbd && !highbank) { + opl3.dam = v >> 7; + opl3.dvb = (v >> 6) & 0x01; + CH_UpdateRhythm(&opl3); + } + else if ((regm & 0x0f) < 9) { + CH_UpdateAB0(&opl3, 9 * highbank + (regm & 0x0f)); + if (v & 0x20) { + CH_Enable(&opl3, 9 * highbank + (regm & 0x0f)); + } + else { + CH_Disable(&opl3, 9 * highbank + (regm & 0x0f)); + } + } + break; + case 0xc0: + if ((regm & 0x0f) < 9) { + CH_UpdateC0(&opl3, 9 * highbank + (regm & 0x0f)); + } + break; + } +} + +void NukedOPL3::Update(float* sndptr, int numsamples) { + Bit32s outa, outb; + Bit8u ii = 0; + for (Bit32u i = 0; i < (Bit32u)numsamples; i++) { + outa = 0; + outb = 0; + for (ii = 0; ii < 36; ii++) { + OP_CalcFB(&opl3, ii); + } + CH_GenerateRhythm(&opl3); + for (ii = 0; ii < 18; ii++) { + CH_Generate(&opl3, ii); + if (FullPan) { + outa += (Bit16s)(opl3.Channels[ii].out * opl3.Channels[ii].panl); + outb += (Bit16s)(opl3.Channels[ii].out * opl3.Channels[ii].panr); + } + else { + outa += (Bit16s)(opl3.Channels[ii].out & opl3.Channels[ii].cha); + outb += (Bit16s)(opl3.Channels[ii].out & opl3.Channels[ii].chb); + } + } + for (ii = 0; ii < 36; ii++) { + EG_Generate(&opl3, ii); + PG_Generate(&opl3, ii); + } + N_Generate(&opl3); + opl3.trem_inc++; + if (!(opl3.trem_inc & 0x3f)) { + if (!opl3.trem_dir) { + if (opl3.trem_tval == 105) { + opl3.trem_tval--; + opl3.trem_dir = 1; + } + else { + opl3.trem_tval++; + } + } + else { + if (opl3.trem_tval == 0) { + opl3.trem_tval++; + opl3.trem_dir = 0; + } + else { + opl3.trem_tval--; + } + } + opl3.trem_val = (opl3.trem_tval >> 2) >> ((!opl3.dam) << 1); + } + opl3.timer++; + opl3.vib_pos = (opl3.timer >> 10) & 0x07; + *sndptr++ += (float)(outa / 10240.0); + *sndptr++ += (float)(outb / 10240.0); + } +} + +void NukedOPL3::SetPanning(int c, float left, float right) { + if (FullPan) { + opl3.Channels[c].panl = left; + opl3.Channels[c].panr = right; + } +} + +NukedOPL3::NukedOPL3(bool stereo) { + FullPan = stereo; + Reset(); +} + +OPLEmul *NukedOPL3Create(bool stereo) { + return new NukedOPL3(stereo); +} diff --git a/src/oplsynth/nukedopl3.h b/src/oplsynth/nukedopl3.h new file mode 100644 index 000000000..15f264c1a --- /dev/null +++ b/src/oplsynth/nukedopl3.h @@ -0,0 +1,120 @@ +/* +* Copyright (C) 2013-2014 Nuke.YKT +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Nuked Yamaha YMF262(aka OPL3) emulator. + Thanks: + MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + Feedback and Rhythm part calculation information. + forums.submarine.org.uk(carbon14, opl3): + Tremolo and phase generator calculation information. + OPLx decapsulated(Matthew Gambrell and Olli Niemitalo): + OPL2 ROMs. +*/ + +//version 1.4.2 + +#include "opl.h" +#include "muslib.h" + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef DWORD Bit32u; +typedef SDWORD Bit32s; +typedef WORD Bit16u; +typedef SWORD Bit16s; +typedef BYTE Bit8u; +typedef SBYTE Bit8s; + +struct channel { + Bit8u con; + Bit8u chtype; + Bit8u alg; + Bit16u offset; + Bit8u feedback; + Bit16u cha, chb, chc, chd; + Bit16s out; + Bit16u f_number; + Bit8u block; + Bit8u ksv; + float panl; + float panr; +}; + +struct slot { + Bit32u PG_pos; + Bit32u PG_inc; + Bit16s EG_out; + Bit8u eg_inc; + Bit8u eg_gen; + Bit8u eg_gennext; + Bit16u EG_mout; + Bit8u EG_ksl; + Bit8u EG_ar; + Bit8u EG_dr; + Bit8u EG_sl; + Bit8u EG_rr; + Bit8u EG_state; + Bit8u EG_type; + Bit16s out; + Bit16s *mod; + Bit16s prevout[2]; + Bit16s fbmod; + Bit16u offset; + Bit8u mult; + Bit8u vibrato; + Bit8u tremolo; + Bit8u ksr; + Bit8u EG_tl; + Bit8u ksl; + Bit8u key; + Bit8u waveform; +}; + + +struct chip { + Bit8u opl_memory[0x200]; + Bit8u newm; + Bit8u nts; + Bit8u rhythm; + Bit8u dvb; + Bit8u dam; + Bit32u noise; + Bit16u vib_pos; + Bit16u timer; + Bit8u trem_inc; + Bit8u trem_tval; + Bit8u trem_dir; + Bit8u trem_val; + channel Channels[18]; + slot OPs[36]; + Bit16s zm; +}; + +class NukedOPL3 : public OPLEmul { +private: + chip opl3; + bool FullPan; +public: + void Reset(); + void Update(float* sndptr, int numsamples); + void WriteReg(int reg, int v); + void SetPanning(int c, float left, float right); + + NukedOPL3(bool stereo); +}; diff --git a/src/oplsynth/opl.h b/src/oplsynth/opl.h index 661258a26..2cbb19c32 100644 --- a/src/oplsynth/opl.h +++ b/src/oplsynth/opl.h @@ -20,6 +20,7 @@ public: OPLEmul *YM3812Create(bool stereo); OPLEmul *DBOPLCreate(bool stereo); OPLEmul *JavaOPLCreate(bool stereo); +OPLEmul *NukedOPL3Create(bool stereo); #define OPL_SAMPLE_RATE 49716.0 #define CENTER_PANNING_POWER 0.70710678118 /* [RH] volume at center for EQP */ diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 03de8b3df..2ff70c9e3 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1581,20 +1581,28 @@ void FBehavior::StaticSerializeModuleStates (FArchive &arc) for (modnum = 0; modnum < StaticModules.Size(); ++modnum) { FBehavior *module = StaticModules[modnum]; + int ModSize = module->GetDataSize(); if (arc.IsStoring()) { arc.WriteString (module->ModuleName); + if (SaveVersion >= 4516) arc << ModSize; } else { char *modname = NULL; arc << modname; + if (SaveVersion >= 4516) arc << ModSize; if (stricmp (modname, module->ModuleName) != 0) { delete[] modname; I_Error ("Level was saved with a different set of ACS modules."); } + else if (ModSize != module->GetDataSize()) + { + delete[] modname; + I_Error("ACS module %s has changed from what was saved. (Have %d bytes, save has %d bytes)", module->ModuleName, module->GetDataSize(), ModSize); + } delete[] modname; } module->SerializeVars (arc); @@ -1873,7 +1881,7 @@ FBehavior::FBehavior (int lumpnum, FileReader * fr, int len) funcm->HasReturnValue = funcf->HasReturnValue; funcm->ImportNum = funcf->ImportNum; funcm->LocalCount = funcf->LocalCount; - funcm->Address = funcf->Address; + funcm->Address = LittleLong(funcf->Address); } } @@ -2058,7 +2066,7 @@ FBehavior::FBehavior (int lumpnum, FileReader * fr, int len) const char *const parse = (char *)&chunk[2]; DWORD i; - for (i = 0; i < chunk[1]; ) + for (i = 0; i < LittleLong(chunk[1]); ) { if (parse[i]) { @@ -2351,7 +2359,7 @@ void FBehavior::LoadScriptsDirectory () scripts.b = FindChunk (MAKE_ID('S','F','L','G')); if (scripts.dw != NULL) { - max = scripts.dw[1] / 4; + max = LittleLong(scripts.dw[1]) / 4; scripts.dw += 2; for (i = max; i > 0; --i, scripts.w += 2) { @@ -2367,7 +2375,7 @@ void FBehavior::LoadScriptsDirectory () scripts.b = FindChunk (MAKE_ID('S','V','C','T')); if (scripts.dw != NULL) { - max = scripts.dw[1] / 4; + max = LittleLong(scripts.dw[1]) / 4; scripts.dw += 2; for (i = max; i > 0; --i, scripts.w += 2) { @@ -2681,7 +2689,7 @@ BYTE *FBehavior::FindChunk (DWORD id) const { return chunk; } - chunk += ((DWORD *)chunk)[1] + 8; + chunk += LittleLong(((DWORD *)chunk)[1]) + 8; } return NULL; } @@ -2689,14 +2697,14 @@ BYTE *FBehavior::FindChunk (DWORD id) const BYTE *FBehavior::NextChunk (BYTE *chunk) const { DWORD id = *(DWORD *)chunk; - chunk += ((DWORD *)chunk)[1] + 8; + chunk += LittleLong(((DWORD *)chunk)[1]) + 8; while (chunk != NULL && chunk < Data + DataSize) { if (((DWORD *)chunk)[0] == id) { return chunk; } - chunk += ((DWORD *)chunk)[1] + 8; + chunk += LittleLong(((DWORD *)chunk)[1]) + 8; } return NULL; } @@ -3673,6 +3681,7 @@ enum APROP_AttackZOffset = 40, APROP_StencilColor = 41, APROP_Friction = 42, + APROP_DamageMultiplier=43, }; // These are needed for ACS's APROP_RenderStyle @@ -3862,6 +3871,10 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) actor->DamageFactor = value; break; + case APROP_DamageMultiplier: + actor->DamageMultiply = value; + break; + case APROP_MasterTID: AActor *other; other = SingleActorFromTID (value, NULL); @@ -3933,6 +3946,7 @@ int DLevelScript::GetActorProperty (int tid, int property, const SDWORD *stack, case APROP_Speed: return actor->Speed; case APROP_Damage: return actor->Damage; // Should this call GetMissileDamage() instead? case APROP_DamageFactor:return actor->DamageFactor; + case APROP_DamageMultiplier: return actor->DamageMultiply; case APROP_Alpha: return actor->alpha; case APROP_RenderStyle: for (int style = STYLE_None; style < STYLE_Count; ++style) { // Check for a legacy render style that matches. diff --git a/src/p_acs.h b/src/p_acs.h index 02544e367..88016f0db 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -308,6 +308,7 @@ public: int GetScriptIndex (const ScriptPtr *ptr) const { ptrdiff_t index = ptr - Scripts; return index >= NumScripts ? -1 : (int)index; } ScriptPtr *GetScriptPtr(int index) const { return index >= 0 && index < NumScripts ? &Scripts[index] : NULL; } int GetLumpNum() const { return LumpNum; } + int GetDataSize() const { return DataSize; } const char *GetModuleName() const { return ModuleName; } ACSProfileInfo *GetFunctionProfileData(int index) { return index >= 0 && index < NumFunctions ? &FunctionProfileData[index] : NULL; } ACSProfileInfo *GetFunctionProfileData(ScriptFunction *func) { return GetFunctionProfileData((int)(func - (ScriptFunction *)Functions)); } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 4ee86d82c..36a68281e 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -925,6 +925,11 @@ static inline bool MustForcePain(AActor *target, AActor *inflictor) (inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS)); } +static inline bool isFakePain(AActor *target, AActor *inflictor) +{ + return ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))); +} + // Returns the amount of damage actually inflicted upon the target, or -1 if // the damage was cancelled. @@ -940,6 +945,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, bool justhit = false; bool plrDontThrust = false; bool invulpain = false; + bool fakedPain = false; + bool forcedPain = false; int fakeDamage = 0; int holdDamage = 0; @@ -948,6 +955,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, return -1; } + //Rather than unnecessarily call the function over and over again, let's be a little more efficient. + fakedPain = (isFakePain(target, inflictor)); + forcedPain = (MustForcePain(target, inflictor)); + // Spectral targets only take damage from spectral projectiles. if (target->flags4 & MF4_SPECTRAL && damage < TELEFRAG_DAMAGE) { @@ -976,7 +987,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { if (inflictor == NULL || (!(inflictor->flags3 & MF3_FOILINVUL) && !(flags & DMG_FOILINVUL))) { - if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + if (fakedPain) { invulpain = true; //This returns -1 later. fakeDamage = damage; @@ -991,7 +1002,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // Players are optionally excluded from getting thrust by damage. if (static_cast(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL) { - if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + if (fakedPain) plrDontThrust = 1; else return -1; @@ -999,7 +1010,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } - if (((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) && (damage < TELEFRAG_DAMAGE)) + if ((fakedPain) && (damage < TELEFRAG_DAMAGE)) { //Intentionally do not jump to fakepain because the damage hasn't been dished out yet. //Once it's dished out, THEN we can disregard damage factors affecting pain chances. @@ -1057,24 +1068,31 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (damage > 0) damage = inflictor->DoSpecialDamage (target, damage, mod); - if (damage == -1) + if ((damage == -1) && (target->player == NULL)) //This isn't meant for the player. { - if ((target->flags7 & MF7_ALLOWPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) //Hold off ending the function before we can deal the pain chances. + if (fakedPain) //Hold off ending the function before we can deal the pain chances. goto fakepain; return -1; } } // Handle active damage modifiers (e.g. PowerDamage) - if (source != NULL && source->Inventory != NULL) + if (source != NULL) { int olddam = damage; - source->Inventory->ModifyDamage(olddam, mod, damage, false); - if (olddam != damage && damage <= 0) + + if (source->Inventory != NULL) + { + source->Inventory->ModifyDamage(olddam, mod, damage, false); + } + damage = FixedMul(damage, source->DamageMultiply); + + if (((source->flags7 & MF7_CAUSEPAIN) && (fakeDamage <= 0)) || (olddam != damage && damage <= 0)) { // Still allow FORCEPAIN - if (MustForcePain(target, inflictor)) - { + if (forcedPain) goto dopain; - } + else if (fakedPain) + goto fakepain; + return -1; } } @@ -1083,13 +1101,11 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { int olddam = damage; target->Inventory->ModifyDamage(olddam, mod, damage, true); - if (((target->flags7 & MF7_ALLOWPAIN) && (fakeDamage <= 0)) || (olddam != damage && damage <= 0)) + if ((olddam != damage && damage <= 0) && target->player == NULL) { // Still allow FORCEPAIN and make sure we're still passing along fake damage to hit enemies for their pain states. - if (MustForcePain(target, inflictor)) - { + if (forcedPain) goto dopain; - } - else if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + else if (fakedPain) goto fakepain; return -1; @@ -1103,13 +1119,11 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, mod, target->GetClass()->ActorInfo->DamageFactors); } - if (damage <= 0) + if (damage <= 0 && target->player == NULL) { // Still allow FORCEPAIN - if (MustForcePain(target, inflictor)) - { + if (forcedPain) goto dopain; - } - else if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + else if (fakedPain) goto fakepain; return -1; @@ -1118,9 +1132,9 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (damage > 0) damage = target->TakeSpecialDamage (inflictor, source, damage, mod); } - if (damage == -1) + if (damage == -1 && target->player == NULL) //Make sure it's not a player, the pain has yet to be processed with cheats. { - if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + if (fakedPain) goto fakepain; return -1; @@ -1247,17 +1261,18 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE)) //Absolutely no hurting if NODAMAGE is involved. Same for GODMODE2. { // player is invulnerable, so don't hurt him - - if (((!(player->cheats & CF_GODMODE)) && (!(player->cheats & CF_GODMODE2)) && (!(player->mo->flags5 & MF5_NOPAIN))) && - (((player->mo->flags7 & MF7_ALLOWPAIN) || (player->mo->flags5 & MF5_NODAMAGE)) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN)))) //Make sure no godmodes and NOPAIN flags are found first. //Then, check to see if the player has NODAMAGE or ALLOWPAIN, or inflictor has CAUSEPAIN. - { + if ((player->cheats & CF_GODMODE) || (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NOPAIN)) + return -1; + else if ((((player->mo->flags7 & MF7_ALLOWPAIN) || (player->mo->flags5 & MF5_NODAMAGE)) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN)))) + { invulpain = true; fakeDamage = damage; goto fakepain; } - return -1; + else + return -1; } if (!(flags & DMG_NO_ARMOR) && player->mo->Inventory != NULL) @@ -1306,6 +1321,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // telefrag him right? ;) (Unfortunately the damage is "absorbed" by armor, // but telefragging should still do enough damage to kill the player) // Ignore players that are already dead. + // [MC]Buddha2 absorbs telefrag damage, and anything else thrown their way. if ((player->cheats & CF_BUDDHA2) || (((player->cheats & CF_BUDDHA) || (player->mo->flags7 & MF7_BUDDHA)) && (damage < TELEFRAG_DAMAGE)) && (player->playerstate != PST_DEAD)) { // If this is a voodoo doll we need to handle the real player as well. @@ -1339,7 +1355,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, damage = newdam; if (damage <= 0) { - if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + if (fakedPain) goto fakepain; else return damage; @@ -1370,6 +1386,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (target->health <= 0) { + //[MC]Buddha flag for monsters. if ((target->flags7 & MF7_BUDDHA) && (damage < TELEFRAG_DAMAGE) && ((inflictor == NULL || !(inflictor->flags3 & MF7_FOILBUDDHA)) && !(flags & DMG_FOILBUDDHA))) { //FOILBUDDHA or Telefrag damage must kill it. target->health = 1; @@ -1434,7 +1451,7 @@ fakepain: //Needed so we can skip the rest of the above, but still obey the orig //CAUSEPAIN can always attempt to trigger the chances of pain. //ALLOWPAIN can do the same, only if the (unfiltered aka fake) damage is greater than 0. if ((((target->flags7 & MF7_ALLOWPAIN) && (fakeDamage > 0)) - || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) && (fakeDamage != damage)) + || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN)))) { holdDamage = damage; //Store the modified damage away after factors are taken into account. damage = fakeDamage; //Retrieve the original damage. @@ -1535,7 +1552,7 @@ dopain: { return -1; //NOW we return -1! } - else if ((target->flags7 & MF7_ALLOWPAIN) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))) + else if (fakedPain) { return holdDamage; //This is the calculated damage after all is said and done. } diff --git a/src/p_map.cpp b/src/p_map.cpp index b406dff7b..c0e5c3baa 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -736,8 +736,18 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) else { // Find the point on the line closest to the actor's center, and use // that to calculate openings - SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy) / (1 << 24); + // [EP] Use 64 bit integers in order to keep the exact result of the + // multiplication, because in the worst case, which is by the map limit + // (32767 units, which is 2147418112 in fixed_t notation), the result + // would occupy 62 bits (if I consider also the addition with another + // and possible 62 bit value, it's 63 bits). + // This privilege could not be available if the starting data would be + // 64 bit long. + // With this, the division is exact as the 32 bit float counterpart, + // though I don't know why I had to discard the first 24 bits from the + // divisor. SQWORD r_num = ((SQWORD(tm.x - ld->v1->x)*ld->dx) + (SQWORD(tm.y - ld->v1->y)*ld->dy)); + SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy) / (1 << 24); fixed_t r = (fixed_t)(r_num / r_den); /* Printf ("%d:%d: %d (%d %d %d %d) (%d %d %d %d)\n", level.time, ld-lines, r, ld->frontsector->floorplane.a, diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index c7bfcea21..cf6bccc8a 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -84,7 +84,6 @@ static void PlayerLandedOnThing (AActor *mo, AActor *onmobj); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- -extern cycle_t BotSupportCycles; extern int BotWTG; EXTERN_CVAR (Int, cl_rockettrails) @@ -313,8 +312,16 @@ void AActor::Serialize (FArchive &arc) } arc << lastpush << lastbump << PainThreshold - << DamageFactor - << WeaveIndexXY << WeaveIndexZ + << DamageFactor; + if (SaveVersion >= 4516) + { + arc << DamageMultiply; + } + else + { + DamageMultiply = FRACUNIT; + } + arc << WeaveIndexXY << WeaveIndexZ << PoisonDamageReceived << PoisonDurationReceived << PoisonPeriodReceived << Poisoner << PoisonDamage << PoisonDuration << PoisonPeriod; if (SaveVersion >= 3235) @@ -3868,6 +3875,7 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t actor->touching_sectorlist = NULL; // NULL head of sector list // phares 3/13/98 if (G_SkillProperty(SKILLP_FastMonsters)) actor->Speed = actor->GetClass()->Meta.GetMetaFixed(AMETA_FastSpeed, actor->Speed); + actor->DamageMultiply = FRACUNIT; // set subsector and/or block links diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 53b288798..6b5d9403f 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4175,7 +4175,6 @@ static void P_Shutdown () P_FreeLevelData (); P_FreeExtraLevelData (); ST_Clear(); - bglobal.DestroyAllBots (); } #if 0 diff --git a/src/p_user.cpp b/src/p_user.cpp index 72b92b7d1..bf656d2b9 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3066,7 +3066,6 @@ void player_t::Serialize (FArchive &arc) if (SaveVersion < 4514 && IsBot) { Bot = new DBot; - GC::WriteBarrier (Bot); arc << Bot->angle << Bot->dest @@ -3089,6 +3088,12 @@ void player_t::Serialize (FArchive &arc) << Bot->oldx << Bot->oldy; } + + if (SaveVersion < 4516 && Bot != NULL) + { + Bot->player = this; + } + if (arc.IsLoading ()) { // If the player reloaded because they pressed +use after dying, we diff --git a/src/sdl/i_gui.cpp b/src/sdl/i_gui.cpp new file mode 100644 index 000000000..b40fb7517 --- /dev/null +++ b/src/sdl/i_gui.cpp @@ -0,0 +1,131 @@ + +// Moved from sdl/i_system.cpp + +#include + +#include + +#include "bitmap.h" +#include "v_palette.h" +#include "textures.h" + +extern SDL_Surface *cursorSurface; +extern SDL_Rect cursorBlit; + +#ifdef USE_XCURSOR +// Xlib has its own GC, so don't let it interfere. +#define GC XGC +#include +#undef GC + +bool UseXCursor; +SDL_Cursor *X11Cursor; +SDL_Cursor *FirstCursor; + +// Hack! Hack! SDL does not provide a clean way to get the XDisplay. +// On the other hand, there are no more planned updates for SDL 1.2, +// so we should be fine making assumptions. +struct SDL_PrivateVideoData +{ + int local_X11; + Display *X11_Display; +}; + +struct SDL_VideoDevice +{ + const char *name; + int (*functions[9])(); + SDL_VideoInfo info; + SDL_PixelFormat *displayformatalphapixel; + int (*morefuncs[9])(); + Uint16 *gamma; + int (*somefuncs[9])(); + unsigned int texture; // Only here if SDL was compiled with OpenGL support. Ack! + int is_32bit; + int (*itsafuncs[13])(); + SDL_Surface *surfaces[3]; + SDL_Palette *physpal; + SDL_Color *gammacols; + char *wm_strings[2]; + int offsets[2]; + SDL_GrabMode input_grab; + int handles_any_size; + SDL_PrivateVideoData *hidden; // Why did they have to bury this so far in? +}; + +extern SDL_VideoDevice *current_video; +#define SDL_Display (current_video->hidden->X11_Display) + +SDL_Cursor *CreateColorCursor(FTexture *cursorpic) +{ + return NULL; +} +#endif + +bool I_SetCursor(FTexture *cursorpic) +{ + if (cursorpic != NULL && cursorpic->UseType != FTexture::TEX_Null) + { + // Must be no larger than 32x32. + if (cursorpic->GetWidth() > 32 || cursorpic->GetHeight() > 32) + { + return false; + } + +#ifdef USE_XCURSOR + if (UseXCursor) + { + if (FirstCursor == NULL) + { + FirstCursor = SDL_GetCursor(); + } + X11Cursor = CreateColorCursor(cursorpic); + if (X11Cursor != NULL) + { + SDL_SetCursor(X11Cursor); + return true; + } + } +#endif + if (cursorSurface == NULL) + cursorSurface = SDL_CreateRGBSurface (0, 32, 32, 32, MAKEARGB(0,255,0,0), MAKEARGB(0,0,255,0), MAKEARGB(0,0,0,255), MAKEARGB(255,0,0,0)); + + SDL_ShowCursor(0); + SDL_LockSurface(cursorSurface); + BYTE buffer[32*32*4]; + memset(buffer, 0, 32*32*4); + FBitmap bmp(buffer, 32*4, 32, 32); + cursorpic->CopyTrueColorPixels(&bmp, 0, 0); + memcpy(cursorSurface->pixels, bmp.GetPixels(), 32*32*4); + SDL_UnlockSurface(cursorSurface); + } + else + { + SDL_ShowCursor(1); + + if (cursorSurface != NULL) + { + SDL_FreeSurface(cursorSurface); + cursorSurface = NULL; + } +#ifdef USE_XCURSOR + if (X11Cursor != NULL) + { + SDL_SetCursor(FirstCursor); + SDL_FreeCursor(X11Cursor); + X11Cursor = NULL; + } +#endif + } + return true; +} + +void I_SetMainWindowVisible(bool visible) +{ + +} + +const char* I_GetBackEndName() +{ + return "SDL"; +} diff --git a/src/sdl/i_main.cpp b/src/sdl/i_main.cpp index 27324edf6..7a18fb05f 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -75,6 +75,10 @@ extern "C" int cc_install_handlers(int, char**, int, int*, const char*, int(*)(char*, char*)); +#ifdef __APPLE__ +void Mac_I_FatalError(const char* errortext); +#endif + // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- @@ -235,6 +239,8 @@ static void unprotect_rtext() void I_StartupJoysticks(); void I_ShutdownJoysticks(); +const char* I_GetBackEndName(); + int main (int argc, char **argv) { #if !defined (__APPLE__) @@ -244,8 +250,8 @@ int main (int argc, char **argv) } #endif // !__APPLE__ - printf(GAMENAME" %s - %s - SDL version\nCompiled on %s\n", - GetVersionString(), GetGitTime(), __DATE__); + printf(GAMENAME" %s - %s - %s version\nCompiled on %s\n", + GetVersionString(), GetGitTime(), I_GetBackEndName(), __DATE__); seteuid (getuid ()); std::set_new_handler (NewFailure); @@ -356,6 +362,11 @@ int main (int argc, char **argv) I_ShutdownJoysticks(); if (error.GetMessage ()) fprintf (stderr, "%s\n", error.GetMessage ()); + +#ifdef __APPLE__ + Mac_I_FatalError(error.GetMessage()); +#endif // __APPLE__ + exit (-1); } catch (...) diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index 8fea36c37..41fdef323 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -71,12 +71,9 @@ #include "m_fixed.h" #include "g_level.h" -#ifdef USE_XCURSOR -// Xlib has its own GC, so don't let it interfere. -#define GC XGC -#include -#undef GC -#endif +#ifdef __APPLE__ +#include +#endif // __APPLE__ EXTERN_CVAR (String, language) @@ -91,11 +88,6 @@ extern bool GtkAvailable; #elif defined(__APPLE__) int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad); #endif -#ifdef USE_XCURSOR -bool UseXCursor; -SDL_Cursor *X11Cursor; -SDL_Cursor *FirstCursor; -#endif DWORD LanguageIDs[4]; @@ -122,185 +114,6 @@ void I_EndRead(void) } -static DWORD TicStart; -static DWORD BaseTime; -static int TicFrozen; - -// Signal based timer. -static Semaphore timerWait; -static int tics; -static DWORD sig_start; - -void I_SelectTimer(); - -// [RH] Returns time in milliseconds -unsigned int I_MSTime (void) -{ - unsigned int time = SDL_GetTicks (); - return time - BaseTime; -} - -// Exactly the same thing, but based does no modification to the time. -unsigned int I_FPSTime() -{ - return SDL_GetTicks(); -} - -// -// I_GetTime -// returns time in 1/35th second tics -// -int I_GetTimeSelect (bool saveMS) -{ - I_SelectTimer(); - return I_GetTime (saveMS); -} - -int I_GetTimePolled (bool saveMS) -{ - if (TicFrozen != 0) - { - return TicFrozen; - } - - DWORD tm = SDL_GetTicks(); - - if (saveMS) - { - TicStart = tm; - } - return Scale(tm - BaseTime, TICRATE, 1000); -} - -int I_GetTimeSignaled (bool saveMS) -{ - if (saveMS) - { - TicStart = sig_start; - } - return tics; -} - -int I_WaitForTicPolled (int prevtic) -{ - int time; - - assert (TicFrozen == 0); - while ((time = I_GetTimePolled(false)) <= prevtic) - ; - - return time; -} - -int I_WaitForTicSignaled (int prevtic) -{ - assert (TicFrozen == 0); - - while(tics <= prevtic) - { - SEMAPHORE_WAIT(timerWait) - } - - return tics; -} - -void I_FreezeTimeSelect (bool frozen) -{ - I_SelectTimer(); - return I_FreezeTime (frozen); -} - -void I_FreezeTimePolled (bool frozen) -{ - if (frozen) - { - assert(TicFrozen == 0); - TicFrozen = I_GetTimePolled(false); - } - else - { - assert(TicFrozen != 0); - int froze = TicFrozen; - TicFrozen = 0; - int now = I_GetTimePolled(false); - BaseTime += (now - froze) * 1000 / TICRATE; - } -} - -void I_FreezeTimeSignaled (bool frozen) -{ - TicFrozen = frozen; -} - -int I_WaitForTicSelect (int prevtic) -{ - I_SelectTimer(); - return I_WaitForTic (prevtic); -} - -// -// I_HandleAlarm -// Should be called every time there is an alarm. -// -void I_HandleAlarm (int sig) -{ - if(!TicFrozen) - tics++; - sig_start = SDL_GetTicks(); - SEMAPHORE_SIGNAL(timerWait) -} - -// -// I_SelectTimer -// Sets up the timer function based on if we can use signals for efficent CPU -// usage. -// -void I_SelectTimer() -{ - SEMAPHORE_INIT(timerWait, 0, 0) -#ifndef __sun - signal(SIGALRM, I_HandleAlarm); -#else - struct sigaction alrmaction; - sigaction(SIGALRM, NULL, &alrmaction); - alrmaction.sa_handler = I_HandleAlarm; - sigaction(SIGALRM, &alrmaction, NULL); -#endif - - struct itimerval itv; - itv.it_interval.tv_sec = itv.it_value.tv_sec = 0; - itv.it_interval.tv_usec = itv.it_value.tv_usec = 1000000/TICRATE; - - if (setitimer(ITIMER_REAL, &itv, NULL) != 0) - { - I_GetTime = I_GetTimePolled; - I_FreezeTime = I_FreezeTimePolled; - I_WaitForTic = I_WaitForTicPolled; - } - else - { - I_GetTime = I_GetTimeSignaled; - I_FreezeTime = I_FreezeTimeSignaled; - I_WaitForTic = I_WaitForTicSignaled; - } -} - -// Returns the fractional amount of a tic passed since the most recent tic -fixed_t I_GetTimeFrac (uint32 *ms) -{ - DWORD now = SDL_GetTicks (); - if (ms) *ms = TicStart + (1000 / TICRATE); - if (TicStart == 0) - { - return FRACUNIT; - } - else - { - fixed_t frac = clamp ((now - TicStart)*FRACUNIT*TICRATE/1000, 0, FRACUNIT); - return frac; - } -} - void I_WaitVBL (int count) { // I_WaitVBL is never used to actually synchronize to the @@ -322,6 +135,9 @@ void SetLanguageIDs () LanguageIDs[3] = LanguageIDs[2] = LanguageIDs[1] = LanguageIDs[0] = lang; } +void I_InitTimer (); +void I_ShutdownTimer (); + // // I_Init // @@ -330,11 +146,9 @@ void I_Init (void) CheckCPUID (&CPU); DumpCPUInfo (&CPU); - I_GetTime = I_GetTimeSelect; - I_WaitForTic = I_WaitForTicSelect; - I_FreezeTime = I_FreezeTimeSelect; atterm (I_ShutdownSound); I_InitSound (); + I_InitTimer (); } // @@ -350,6 +164,8 @@ void I_Quit (void) G_CheckDemoStatus(); C_DeinitConsole(); + + I_ShutdownTimer(); } @@ -785,6 +601,10 @@ int I_FindAttr (findstate_t *fileinfo) return 0; } +#ifdef __APPLE__ +static PasteboardRef s_clipboard; +#endif // __APPLE__ + // Clipboard support requires GTK+ // TODO: GTK+ uses UTF-8. We don't, so some conversions would be appropriate. void I_PutInClipboard (const char *str) @@ -805,6 +625,23 @@ void I_PutInClipboard (const char *str) } */ } +#elif defined __APPLE__ + if (NULL == s_clipboard) + { + PasteboardCreate(kPasteboardClipboard, &s_clipboard); + } + + PasteboardClear(s_clipboard); + PasteboardSynchronize(s_clipboard); + + const CFDataRef textData = CFDataCreate(kCFAllocatorDefault, + reinterpret_cast(str), strlen(str)); + + if (NULL != textData) + { + PasteboardPutItemFlavor(s_clipboard, PasteboardItemID(1), + CFSTR("public.utf8-plain-text"), textData, 0); + } #endif } @@ -826,6 +663,61 @@ FString I_GetFromClipboard (bool use_primary_selection) } } } +#elif defined __APPLE__ + FString result; + + if (NULL == s_clipboard) + { + PasteboardCreate(kPasteboardClipboard, &s_clipboard); + } + + PasteboardSynchronize(s_clipboard); + + ItemCount itemCount = 0; + PasteboardGetItemCount(s_clipboard, &itemCount); + + if (0 == itemCount) + { + return FString(); + } + + PasteboardItemID itemID; + + if (0 != PasteboardGetItemIdentifier(s_clipboard, 1, &itemID)) + { + return FString(); + } + + CFArrayRef flavorTypeArray; + + if (0 != PasteboardCopyItemFlavors(s_clipboard, itemID, &flavorTypeArray)) + { + return FString(); + } + + const CFIndex flavorCount = CFArrayGetCount(flavorTypeArray); + + for (CFIndex flavorIndex = 0; flavorIndex < flavorCount; ++flavorIndex) + { + const CFStringRef flavorType = static_cast( + CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex)); + + if (UTTypeConformsTo(flavorType, CFSTR("public.utf8-plain-text"))) + { + CFDataRef flavorData; + + if (0 == PasteboardCopyItemFlavorData(s_clipboard, itemID, flavorType, &flavorData)) + { + result += reinterpret_cast(CFDataGetBytePtr(flavorData)); + } + + CFRelease(flavorData); + } + } + + CFRelease(flavorTypeArray); + + return result; #endif return ""; } @@ -851,104 +743,3 @@ unsigned int I_MakeRNGSeed() } return seed; } - -#ifdef USE_XCURSOR -// Hack! Hack! SDL does not provide a clean way to get the XDisplay. -// On the other hand, there are no more planned updates for SDL 1.2, -// so we should be fine making assumptions. -struct SDL_PrivateVideoData -{ - int local_X11; - Display *X11_Display; -}; - -struct SDL_VideoDevice -{ - const char *name; - int (*functions[9])(); - SDL_VideoInfo info; - SDL_PixelFormat *displayformatalphapixel; - int (*morefuncs[9])(); - Uint16 *gamma; - int (*somefuncs[9])(); - unsigned int texture; // Only here if SDL was compiled with OpenGL support. Ack! - int is_32bit; - int (*itsafuncs[13])(); - SDL_Surface *surfaces[3]; - SDL_Palette *physpal; - SDL_Color *gammacols; - char *wm_strings[2]; - int offsets[2]; - SDL_GrabMode input_grab; - int handles_any_size; - SDL_PrivateVideoData *hidden; // Why did they have to bury this so far in? -}; - -extern SDL_VideoDevice *current_video; -#define SDL_Display (current_video->hidden->X11_Display) - -SDL_Cursor *CreateColorCursor(FTexture *cursorpic) -{ - return NULL; -} -#endif - -SDL_Surface *cursorSurface = NULL; -SDL_Rect cursorBlit = {0, 0, 32, 32}; -bool I_SetCursor(FTexture *cursorpic) -{ - if (cursorpic != NULL && cursorpic->UseType != FTexture::TEX_Null) - { - // Must be no larger than 32x32. - if (cursorpic->GetWidth() > 32 || cursorpic->GetHeight() > 32) - { - return false; - } - -#ifdef USE_XCURSOR - if (UseXCursor) - { - if (FirstCursor == NULL) - { - FirstCursor = SDL_GetCursor(); - } - X11Cursor = CreateColorCursor(cursorpic); - if (X11Cursor != NULL) - { - SDL_SetCursor(X11Cursor); - return true; - } - } -#endif - if (cursorSurface == NULL) - cursorSurface = SDL_CreateRGBSurface (0, 32, 32, 32, MAKEARGB(0,255,0,0), MAKEARGB(0,0,255,0), MAKEARGB(0,0,0,255), MAKEARGB(255,0,0,0)); - - SDL_ShowCursor(0); - SDL_LockSurface(cursorSurface); - BYTE buffer[32*32*4]; - memset(buffer, 0, 32*32*4); - FBitmap bmp(buffer, 32*4, 32, 32); - cursorpic->CopyTrueColorPixels(&bmp, 0, 0); - memcpy(cursorSurface->pixels, bmp.GetPixels(), 32*32*4); - SDL_UnlockSurface(cursorSurface); - } - else - { - SDL_ShowCursor(1); - - if (cursorSurface != NULL) - { - SDL_FreeSurface(cursorSurface); - cursorSurface = NULL; - } -#ifdef USE_XCURSOR - if (X11Cursor != NULL) - { - SDL_SetCursor(FirstCursor); - SDL_FreeCursor(X11Cursor); - X11Cursor = NULL; - } -#endif - } - return true; -} diff --git a/src/sdl/i_timer.cpp b/src/sdl/i_timer.cpp new file mode 100644 index 000000000..e3f9906b6 --- /dev/null +++ b/src/sdl/i_timer.cpp @@ -0,0 +1,206 @@ + +// Moved from sdl/i_system.cpp + +#include +#include +#include + +#include + +#include "basictypes.h" +#include "basicinlines.h" +#include "hardware.h" +#include "i_system.h" +#include "templates.h" + + +static DWORD TicStart; +static DWORD BaseTime; +static int TicFrozen; + +// Signal based timer. +static Semaphore timerWait; +static int tics; +static DWORD sig_start; + +void I_SelectTimer(); + +// [RH] Returns time in milliseconds +unsigned int I_MSTime (void) +{ + unsigned int time = SDL_GetTicks (); + return time - BaseTime; +} + +// Exactly the same thing, but based does no modification to the time. +unsigned int I_FPSTime() +{ + return SDL_GetTicks(); +} + +// +// I_GetTime +// returns time in 1/35th second tics +// +int I_GetTimeSelect (bool saveMS) +{ + I_SelectTimer(); + return I_GetTime (saveMS); +} + +int I_GetTimePolled (bool saveMS) +{ + if (TicFrozen != 0) + { + return TicFrozen; + } + + DWORD tm = SDL_GetTicks(); + + if (saveMS) + { + TicStart = tm; + } + return Scale(tm - BaseTime, TICRATE, 1000); +} + +int I_GetTimeSignaled (bool saveMS) +{ + if (saveMS) + { + TicStart = sig_start; + } + return tics; +} + +int I_WaitForTicPolled (int prevtic) +{ + int time; + + assert (TicFrozen == 0); + while ((time = I_GetTimePolled(false)) <= prevtic) + ; + + return time; +} + +int I_WaitForTicSignaled (int prevtic) +{ + assert (TicFrozen == 0); + + while(tics <= prevtic) + { + SEMAPHORE_WAIT(timerWait) + } + + return tics; +} + +void I_FreezeTimeSelect (bool frozen) +{ + I_SelectTimer(); + return I_FreezeTime (frozen); +} + +void I_FreezeTimePolled (bool frozen) +{ + if (frozen) + { + assert(TicFrozen == 0); + TicFrozen = I_GetTimePolled(false); + } + else + { + assert(TicFrozen != 0); + int froze = TicFrozen; + TicFrozen = 0; + int now = I_GetTimePolled(false); + BaseTime += (now - froze) * 1000 / TICRATE; + } +} + +void I_FreezeTimeSignaled (bool frozen) +{ + TicFrozen = frozen; +} + +int I_WaitForTicSelect (int prevtic) +{ + I_SelectTimer(); + return I_WaitForTic (prevtic); +} + +// +// I_HandleAlarm +// Should be called every time there is an alarm. +// +void I_HandleAlarm (int sig) +{ + if(!TicFrozen) + tics++; + sig_start = SDL_GetTicks(); + SEMAPHORE_SIGNAL(timerWait) +} + +// +// I_SelectTimer +// Sets up the timer function based on if we can use signals for efficent CPU +// usage. +// +void I_SelectTimer() +{ + SEMAPHORE_INIT(timerWait, 0, 0) +#ifndef __sun + signal(SIGALRM, I_HandleAlarm); +#else + struct sigaction alrmaction; + sigaction(SIGALRM, NULL, &alrmaction); + alrmaction.sa_handler = I_HandleAlarm; + sigaction(SIGALRM, &alrmaction, NULL); +#endif + + struct itimerval itv; + itv.it_interval.tv_sec = itv.it_value.tv_sec = 0; + itv.it_interval.tv_usec = itv.it_value.tv_usec = 1000000/TICRATE; + + if (setitimer(ITIMER_REAL, &itv, NULL) != 0) + { + I_GetTime = I_GetTimePolled; + I_FreezeTime = I_FreezeTimePolled; + I_WaitForTic = I_WaitForTicPolled; + } + else + { + I_GetTime = I_GetTimeSignaled; + I_FreezeTime = I_FreezeTimeSignaled; + I_WaitForTic = I_WaitForTicSignaled; + } +} + +// Returns the fractional amount of a tic passed since the most recent tic +fixed_t I_GetTimeFrac (uint32 *ms) +{ + DWORD now = SDL_GetTicks (); + if (ms) *ms = TicStart + (1000 / TICRATE); + if (TicStart == 0) + { + return FRACUNIT; + } + else + { + fixed_t frac = clamp ((now - TicStart)*FRACUNIT*TICRATE/1000, 0, FRACUNIT); + return frac; + } +} + +void I_InitTimer () +{ + I_GetTime = I_GetTimeSelect; + I_WaitForTic = I_WaitForTicSelect; + I_FreezeTime = I_FreezeTimeSelect; +} + +void I_ShutdownTimer () +{ + +} diff --git a/src/sdl/iwadpicker_cocoa.mm b/src/sdl/iwadpicker_cocoa.mm index ccf2ebceb..d1364fa81 100644 --- a/src/sdl/iwadpicker_cocoa.mm +++ b/src/sdl/iwadpicker_cocoa.mm @@ -33,9 +33,29 @@ ** */ +// Avoid collision between DObject class and Objective-C +#define Class ObjectClass + +#include "cmdlib.h" #include "d_main.h" #include "version.h" +#include "c_cvars.h" +#include "m_argv.h" +#include "m_misc.h" +#include "gameconfigfile.h" + +#undef Class + #include +#include +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 +// Missing type definition for 10.4 and earlier +typedef unsigned int NSUInteger; +#endif // prior to 10.5 + +CVAR(String, osx_additional_parameters, "", CVAR_ARCHIVE | CVAR_NOSET | CVAR_GLOBALCONFIG); enum { @@ -107,6 +127,45 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; @end +static NSDictionary* GetKnownFileTypes() +{ + return [NSDictionary dictionaryWithObjectsAndKeys: + @"-file" , @"wad", + @"-file" , @"pk3", + @"-file" , @"zip", + @"-file" , @"pk7", + @"-file" , @"7z", + @"-deh" , @"deh", + @"-bex" , @"bex", + @"-exec" , @"cfg", + @"-playdemo", @"lmp", + nil]; +} + +static NSArray* GetKnownExtensions() +{ + return [GetKnownFileTypes() allKeys]; +} + +@interface NSMutableString(AppendKnownFileType) +- (void)appendKnownFileType:(NSString *)filePath; +@end + +@implementation NSMutableString(AppendKnownFileType) +- (void)appendKnownFileType:(NSString *)filePath +{ + NSString* extension = [[filePath pathExtension] lowercaseString]; + NSString* parameter = [GetKnownFileTypes() objectForKey:extension]; + + if (nil == parameter) + { + return; + } + + [self appendFormat:@"%@ \"%@\" ", parameter, filePath]; +} +@end + // So we can listen for button actions and such we need to have an Obj-C class. @interface IWADPicker : NSObject { @@ -114,13 +173,18 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; NSWindow *window; NSButton *okButton; NSButton *cancelButton; + NSButton *browseButton; + NSTextField *parametersTextField; bool cancelled; } - (void)buttonPressed:(id) sender; +- (void)browseButtonPressed:(id) sender; - (void)doubleClicked:(id) sender; - (void)makeLabel:(NSTextField *)label withString:(const char*) str; - (int)pickIWad:(WadStuff *)wads num:(int) numwads showWindow:(bool) showwin defaultWad:(int) defaultiwad; +- (NSString*)commandLineParameters; +- (void)menuActionSent:(NSNotification*)notification; @end @implementation IWADPicker @@ -134,6 +198,52 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; [app stopModal]; } +- (void)browseButtonPressed:(id) sender +{ + NSOpenPanel* openPanel = [NSOpenPanel openPanel]; + [openPanel setAllowsMultipleSelection:YES]; + [openPanel setCanChooseFiles:YES]; + [openPanel setCanChooseDirectories:YES]; + [openPanel setResolvesAliases:YES]; + [openPanel setAllowedFileTypes:GetKnownExtensions()]; + + if (NSOKButton == [openPanel runModal]) + { + NSArray* files = [openPanel URLs]; + NSMutableString* parameters = [NSMutableString string]; + + for (NSUInteger i = 0, ei = [files count]; i < ei; ++i) + { + NSString* filePath = [[files objectAtIndex:i] path]; + BOOL isDirectory = false; + + if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory] && isDirectory) + { + [parameters appendFormat:@"-file \"%@\" ", filePath]; + } + else + { + [parameters appendKnownFileType:filePath]; + } + } + + if ([parameters length] > 0) + { + NSString* newParameters = [parametersTextField stringValue]; + + if ([newParameters length] > 0 + && NO == [newParameters hasSuffix:@" "]) + { + newParameters = [newParameters stringByAppendingString:@" "]; + } + + newParameters = [newParameters stringByAppendingString:parameters]; + + [parametersTextField setStringValue: newParameters]; + } + } +} + - (void)doubleClicked:(id) sender { if ([sender clickedRow] >= 0) @@ -159,20 +269,18 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; cancelled = false; app = [NSApplication sharedApplication]; - id windowTitle = [NSString stringWithFormat:@"%s %s", GAMESIG, GetVersionString()]; + id windowTitle = [NSString stringWithFormat:@"%s %s", GAMENAME, GetVersionString()]; NSRect frame = NSMakeRect(0, 0, 440, 450); window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]; [window setTitle:windowTitle]; - NSTextField *description = [[NSTextField alloc] initWithFrame:NSMakeRect(22, 379, 412, 50)]; - [self makeLabel:description withString:"GZDoom found more than one IWAD\nSelect from the list below to determine which one to use:"]; + NSTextField *description = [[NSTextField alloc] initWithFrame:NSMakeRect(18, 384, 402, 50)]; + [self makeLabel:description withString:GAMENAME " found more than one IWAD\nSelect from the list below to determine which one to use:"]; [[window contentView] addSubview:description]; [description release]; - // Commented out version would account for an additional parameters box. - //NSScrollView *iwadScroller = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 103, 412, 288)]; - NSScrollView *iwadScroller = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 50, 412, 341)]; + NSScrollView *iwadScroller = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 135, 402, 256)]; NSTableView *iwadTable = [[NSTableView alloc] initWithFrame:[iwadScroller bounds]]; IWADTableData *tableData = [[IWADTableData alloc] init:wads num:numwads]; for(int i = 0;i < NUM_COLUMNS;i++) @@ -200,11 +308,12 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; [iwadTable release]; [iwadScroller release]; - /*NSTextField *additionalParametersLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(17, 78, 144, 17)]; - [self makeLabel:additionalParametersLabel:"Additional Parameters"]; + NSTextField *additionalParametersLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(18, 108, 144, 17)]; + [self makeLabel:additionalParametersLabel withString:"Additional Parameters:"]; [[window contentView] addSubview:additionalParametersLabel]; - NSTextField *additionalParameters = [[NSTextField alloc] initWithFrame:NSMakeRect(20, 48, 360, 22)]; - [[window contentView] addSubview:additionalParameters];*/ + parametersTextField = [[NSTextField alloc] initWithFrame:NSMakeRect(20, 48, 402, 54)]; + [parametersTextField setStringValue:[NSString stringWithUTF8String:osx_additional_parameters]]; + [[window contentView] addSubview:parametersTextField]; // Doesn't look like the SDL version implements this so lets not show it. /*NSButton *dontAsk = [[NSButton alloc] initWithFrame:NSMakeRect(18, 18, 178, 18)]; @@ -213,39 +322,164 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; [dontAsk setState:(showwin ? NSOffState : NSOnState)]; [[window contentView] addSubview:dontAsk];*/ - okButton = [[NSButton alloc] initWithFrame:NSMakeRect(236, 12, 96, 32)]; - [okButton setTitle:[NSString stringWithUTF8String:"OK"]]; + okButton = [[NSButton alloc] initWithFrame:NSMakeRect(236, 8, 96, 32)]; + [okButton setTitle:@"OK"]; [okButton setBezelStyle:NSRoundedBezelStyle]; [okButton setAction:@selector(buttonPressed:)]; [okButton setTarget:self]; [okButton setKeyEquivalent:@"\r"]; [[window contentView] addSubview:okButton]; - cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(332, 12, 96, 32)]; - [cancelButton setTitle:[NSString stringWithUTF8String:"Cancel"]]; + cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(332, 8, 96, 32)]; + [cancelButton setTitle:@"Cancel"]; [cancelButton setBezelStyle:NSRoundedBezelStyle]; [cancelButton setAction:@selector(buttonPressed:)]; [cancelButton setTarget:self]; [cancelButton setKeyEquivalent:@"\033"]; [[window contentView] addSubview:cancelButton]; + + browseButton = [[NSButton alloc] initWithFrame:NSMakeRect(14, 8, 96, 32)]; + [browseButton setTitle:@"Browse..."]; + [browseButton setBezelStyle:NSRoundedBezelStyle]; + [browseButton setAction:@selector(browseButtonPressed:)]; + [browseButton setTarget:self]; + [[window contentView] addSubview:browseButton]; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center addObserver:self selector:@selector(menuActionSent:) name:NSMenuDidSendActionNotification object:nil]; [window center]; [app runModalForWindow:window]; + [center removeObserver:self name:NSMenuDidSendActionNotification object:nil]; + [window release]; [okButton release]; [cancelButton release]; + [browseButton release]; return cancelled ? -1 : [iwadTable selectedRow]; } +- (NSString*)commandLineParameters +{ + return [parametersTextField stringValue]; +} + +- (void)menuActionSent:(NSNotification*)notification +{ + NSDictionary* userInfo = [notification userInfo]; + NSMenuItem* menuItem = [userInfo valueForKey:@"MenuItem"]; + + if ( @selector(terminate:) == [menuItem action] ) + { + exit(0); + } +} + @end + +EXTERN_CVAR(String, defaultiwad) + +static NSString* GetArchitectureString() +{ +#ifdef __i386__ + return @"i386"; +#elif defined __x86_64__ + return @"x86_64"; +#elif defined __ppc__ + return @"ppc"; +#elif defined __ppc64__ + return @"ppc64"; +#endif +} + +static void RestartWithParameters(const char* iwadPath, NSString* parameters) +{ + assert(nil != parameters); + + defaultiwad = ExtractFileBase(iwadPath); + + GameConfig->DoGameSetup("Doom"); + M_SaveDefaults(NULL); + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + @try + { + const int commandLineParametersCount = Args->NumArgs(); + assert(commandLineParametersCount > 0); + + NSString* executablePath = [NSString stringWithUTF8String:Args->GetArg(0)]; + NSString* architecture = GetArchitectureString(); + + NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:commandLineParametersCount + 6]; + [arguments addObject:@"-arch"]; + [arguments addObject:architecture]; + [arguments addObject:executablePath]; + [arguments addObject:@"-wad_picker_restart"]; + [arguments addObject:@"-iwad"]; + [arguments addObject:[NSString stringWithUTF8String:iwadPath]]; + + for (int i = 1; i < commandLineParametersCount; ++i) + { + NSString* currentParameter = [NSString stringWithUTF8String:Args->GetArg(i)]; + [arguments addObject:currentParameter]; + } + + wordexp_t expansion = {}; + + if (0 == wordexp([parameters UTF8String], &expansion, 0)) + { + for (size_t i = 0; i < expansion.we_wordc; ++i) + { + NSString* argumentString = [NSString stringWithCString:expansion.we_wordv[i] + encoding:NSUTF8StringEncoding]; + [arguments addObject:argumentString]; + } + + wordfree(&expansion); + } + + [NSTask launchedTaskWithLaunchPath:@"/usr/bin/arch" arguments:arguments]; + + _exit(0); // to avoid atexit()'s functions + } + @catch (NSException* e) + { + NSLog(@"Cannot restart: %@", [e reason]); + } + + [pool release]; +} + +void I_SetMainWindowVisible(bool visible); + // Simple wrapper so we can call this from outside. int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad) { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + I_SetMainWindowVisible(false); + IWADPicker *picker = [IWADPicker alloc]; int ret = [picker pickIWad:wads num:numwads showWindow:showwin defaultWad:defaultiwad]; - [picker release]; + + I_SetMainWindowVisible(true); + + NSString* parametersToAppend = [picker commandLineParameters]; + osx_additional_parameters = [parametersToAppend UTF8String]; + + if (ret >= 0) + { + if (0 != [parametersToAppend length]) + { + RestartWithParameters(wads[ret].Path, parametersToAppend); + } + } + + [pool release]; + return ret; } diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index 92741d325..e4222633d 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -15,6 +15,10 @@ #include +#ifdef __APPLE__ +#include +#endif // __APPLE__ + // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- @@ -43,6 +47,8 @@ public: friend class SDLVideo; + virtual void SetVSync (bool vsync); + private: PalEntry SourcePalette[256]; BYTE GammaTable[3][256]; @@ -75,13 +81,15 @@ struct MiniModeInfo // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern IVideo *Video; -extern SDL_Surface *cursorSurface; -extern SDL_Rect cursorBlit; extern bool GUICapture; +SDL_Surface *cursorSurface = NULL; +SDL_Rect cursorBlit = {0, 0, 32, 32}; + EXTERN_CVAR (Float, Gamma) EXTERN_CVAR (Int, vid_maxfps) EXTERN_CVAR (Bool, cl_capfps) +EXTERN_CVAR (Bool, vid_vsync) // PUBLIC DATA DEFINITIONS ------------------------------------------------- @@ -326,6 +334,7 @@ SDLFB::SDLFB (int width, int height, bool fullscreen) } memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); UpdateColors (); + SetVSync (vid_vsync); } SDLFB::~SDLFB () @@ -535,6 +544,26 @@ bool SDLFB::IsFullscreen () return (Screen->flags & SDL_FULLSCREEN) != 0; } +void SDLFB::SetVSync (bool vsync) +{ +#ifdef __APPLE__ + if (CGLContextObj context = CGLGetCurrentContext()) + { + // Apply vsync for native backend only (where OpenGL context is set) + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 + // Inconsistency between 10.4 and 10.5 SDKs: + // third argument of CGLSetParameter() is const long* on 10.4 and const GLint* on 10.5 + // So, GLint typedef'ed to long instead of int to workaround this issue + typedef long GLint; +#endif // prior to 10.5 + + const GLint value = vsync ? 1 : 0; + CGLSetParameter(context, kCGLCPSwapInterval, &value); + } +#endif // __APPLE__ +} + ADD_STAT (blit) { FString out; diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index 4acda49b9..38ba8557e 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -433,7 +433,7 @@ bool TimidityPPMIDIDevice::LaunchTimidity () } int forkres; - wordexp_t words; + wordexp_t words = {}; switch (wordexp (CommandLine.GetChars(), &words, 0)) { diff --git a/src/statnums.h b/src/statnums.h index 344a328c8..25f644d1d 100644 --- a/src/statnums.h +++ b/src/statnums.h @@ -63,6 +63,7 @@ enum STAT_SECTOREFFECT, // All sector effects that cause floor and ceiling movement STAT_ACTORMOVER, // actor movers STAT_SCRIPTS, // The ACS thinker. This is to ensure that it can't tick before all actors called PostBeginPlay + STAT_BOT, // Bot thinker }; #endif \ No newline at end of file diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index c388a8544..5225b787a 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -266,11 +266,11 @@ enum EDefinitionType #define GCC_MSEG #else #define MSVC_ASEG -#define GCC_ASEG __attribute__((section(SECTION_AREG))) +#define GCC_ASEG __attribute__((section(SECTION_AREG))) __attribute__((used)) #define MSVC_PSEG -#define GCC_PSEG __attribute__((section(SECTION_GREG))) +#define GCC_PSEG __attribute__((section(SECTION_GREG))) __attribute__((used)) #define MSVC_MSEG -#define GCC_MSEG __attribute__((section(SECTION_MREG))) +#define GCC_MSEG __attribute__((section(SECTION_MREG))) __attribute__((used)) #endif diff --git a/src/version.h b/src/version.h index ea163d0a8..158d78486 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4515 +#define SAVEVER 4516 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 08a1c4f4b..4f9e9ae22 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -2,9 +2,9 @@ cmake_minimum_required( VERSION 2.4 ) add_subdirectory( lemon ) add_subdirectory( re2c ) -if( WIN32 ) +if( WIN32 AND NOT CMAKE_SIZEOF_VOID_P MATCHES "8" ) add_subdirectory( fixrtext ) -endif( WIN32 ) +endif( WIN32 AND NOT CMAKE_SIZEOF_VOID_P MATCHES "8" ) add_subdirectory( updaterevision ) add_subdirectory( zipdir ) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 408e534a8..abc654252 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1478,6 +1478,7 @@ OptionValue OplCores 0, "MAME OPL2" 1, "DOSBox OPL3" 2, "Java OPL3" + 3, "Nuked OPL3" } OptionMenu AdvSoundOptions diff --git a/zdoom.vcproj b/zdoom.vcproj index cabc6c84f..21aaccb8e 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -2632,6 +2632,14 @@ RelativePath=".\src\oplsynth\opl_mus_player.h" > + + + +