/* *@@sourcefile syssound.c: * this has code for querying and manipulating the * system sounds, which are stored in MMPM.INI in * the MMOS2 directory. Also, we have support for * manipulating Warp 4 sound schemes in here too. * This code works on Warp 3 also. * * Usage: All OS/2 programs. * * Some of this code used to be in XWorkplace's main\common.c, * as far as the regular system sounds were concerned. * However, all this code has been greatly reworked * and extended with V0.9.0. * * About system sound configuration data * * The current system sounds are stored in ?:\MMOS2\MMPM.INI. * * These are the general flags in the "MMPM2_AlarmSoundsData" * application: * * -- If "EnableSounds" is FALSE, all system sounds are disabled. * This defaults to TRUE (tested). * * -- If "ApplyVolumeToAll" is TRUE, the same volume is used for * all sounds. This defaults to FALSE (tested). * * -- If ApplyVolumeToAll is TRUE, "Volume" is used for the * global volume. Otherwise, the individual sound volumes * (below) will be used. * * "MMPM2_AlarmSounds" then has all the system sounds. The keys * in that application are numerical indices, which are listed * in syssound.h. * * Each sound data block in there consists of three elements: * + soundfile#description#volume * * where "description" is what is listed in the "Sound" object. * "volume" is only used when "ApplyVolumeToAll" (above) is FALSE. * * We have functions in this code file for decoding/setting this * (sndParseSoundData, sndQuerySystemSound, sndWriteSoundData, * sndSetSystemSound). * * By contrast (and for no real reason), Warp 4 stores the * "sound schemes" in OS2SYS.INI. I stuck with that for compatibility, * although this prevents several users from having different * sound schemes. What the heck. * * Anyways, the "PM_SOUND_SCHEMES_LIST" application is a directory of * schemes, which in turn point to other applications in OS2SYS.INI * which have the actual sound scheme data. All these applications * appear to start with "PM_SOUND_xxx" by convention. * * Note that as opposed to the sound data in MMPM.INI, only the * sound file name is stored here (not the three elements as * described above). We slightly extend that mechanism to add an * additional volume data field after the first null byte. This * allows the default WPSound object to still be able to read * the sound scheme data (it apparently uses PrfQueryProfileString) * while the XWorkplace sound object replacement (XWPSound) can * still store volume data as well. * * I have created more functions in this code file to easily load and * store sound schemes (sndDoesSchemeExist, sndCreateSoundScheme, * sndLoadSoundScheme, sndDestroySoundScheme). * * Note: Version numbering in this file relates to XWorkplace version * numbering. * *@@header "helpers\syssound.h" *@@added V0.9.0 [umoeller] */ #define OS2EMX_PLAIN_CHAR // this is needed for "os2emx.h"; if this is defined, // emx will define PSZ as _signed_ char, otherwise // as unsigned char #define INCL_DOSERRORS #define INCL_WINSHELLDATA #include #include #include #include #include "setup.h" // code generation and debugging options #include "helpers\dosh.h" #include "helpers\prfh.h" #include "helpers\syssound.h" #pragma hdrstop /* *@@category: Helpers\Profile (INI) helpers\System sounds * see syssound.c. */ /* *@@ sndParseSoundData: * this helper func splits the system sound data * passed to it into three buffers. * Each key data in there has the following format: + soundfile#description#volume. * * This is copied into the three specified buffers. * You can set any buffer pointer to NULL if you're * not interested in that data. * * Returns the number of items successfully parsed, * which should be 3. * *@@added V0.9.0 [umoeller] *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized */ ULONG sndParseSoundData(PCSZ pszSoundData, // in: INI data from MMPM.INI PSZ pszDescr, // out: sound description, as displayed // in the "Sound" object (ptr may be NULL) PSZ pszFile, // out: sound file to (ptr may be NULL) PULONG pulVolume) // out: sound volume (0-100). Note: // this always returns the individual sound volume, // even if "Global volume" is set in MMPM.INI. { PCSZ p1 = pszSoundData, p2; ULONG ulrc = 0; // get sound file if (p2 = strchr(p1, '#')) { ulrc++; if (pszFile) { strncpy(pszFile, p1, p2 - p1); pszFile[p2-p1] = '\0'; } p1 = p2+1; // get sound description if (p2 = strchr(p1, '#')) { ulrc++; if (pszDescr) { strncpy(pszDescr, p1, p2-p1); pszDescr[p2-p1] = '\0'; } p1 = p2+1; // get volume (0-100) if (pulVolume) { // individual volume settings per sound *pulVolume = atoi(p1); // V0.9.20 (2002-07-03) [umoeller] // sscanf(p1, "%lu", pulVolume); ulrc++; } } } return ulrc; } /* *@@ sndQueryMmpmIniPath: * writes the full path of MMPM.INI into * the specified buffer (e.g. C:\MMOS2\MMPM.INI). * *@@added V0.9.10 (2001-04-16) [umoeller] */ VOID sndQueryMmpmIniPath(PSZ pszMMPM) // out: fully q'fied MMPM.INI { PSZ pszMMPMPath; if (pszMMPMPath = getenv("MMBASE")) // V0.9.6 (2000-10-16) [umoeller] { // variable set: PSZ p; strcpy(pszMMPM, pszMMPMPath); // V0.9.7 (2000-12-17) [umoeller] // kill semicolon if present if (p = strchr(pszMMPM, ';')) *p = 0; strcat(pszMMPM, "\\MMPM.INI"); } else // variable not set (shouldn't happen): try boot drive sprintf(pszMMPM, "%c:\\MMOS2\\MMPM.INI", doshQueryBootDrive()); } /* *@@ sndOpenMmpmIni: * this opens \MMOS2\MMPM.INI on the * boot drive and returns the profile * handle (or NULLHANDLE upon errors). * Use PrfCloseProfile to close the * profile again. * *@@added V0.9.1 (99-12-19) [umoeller] *@@changed V0.9.6 (2000-10-16) [umoeller]: now using MMBASE environment variable *@@changed V0.9.6 (2000-10-16) [umoeller]: added proper HAB param *@@changed V0.9.7 (2000-12-17) [umoeller]: fixed broken MMBASE handling */ HINI sndOpenMmpmIni(HAB hab) { HAB habDesktop = WinQueryAnchorBlock(HWND_DESKTOP); CHAR szMMPM[CCHMAXPATH]; sndQueryMmpmIniPath(szMMPM); return PrfOpenProfile(habDesktop, szMMPM); } /* *@@ sndQuerySystemSound: * this gets a system sound from the MMPM.INI file. * usIndex must be the sound to query. The following * MMSOUND_* IDs are declared in syssound.h: * * Default system sounds (syssound.h): * -- MMSOUND_WARNING 0 * -- MMSOUND_INFORMATION 1 * -- MMSOUND_ERROR 2 * -- MMSOUND_ANIMATEOPEN 3 * -- MMSOUND_ANIMATECLOSE 4 * -- MMSOUND_DRAG 5 * -- MMSOUND_DROP 6 * -- MMSOUND_SYSTEMSTARTUP 7 * -- MMSOUND_SHUTDOWN 8 * -- MMSOUND_SHREDDER 9 * -- MMSOUND_LOCKUP 10 * -- MMSOUND_ALARMCLOCK 11 * -- MMSOUND_PRINTERROR 12 * * BTW, these values match those of the WinAlarm call * (WA_* values), but only the first three are documented * in PMREF and pmwin.h (WA_WARNING, WA_NOTE, WA_ERROR). * * New XWorkplace system sounds: * -- MMSOUND_XFLD_SHUTDOWN 555 * -- MMSOUND_XFLD_RESTARTWPS 556 * -- MMSOUND_XFLD_CTXTOPEN 558 * -- MMSOUND_XFLD_CTXTSELECT 559 * -- MMSOUND_XFLD_CNRDBLCLK 560 * * The string buffers are recommended to be at least * CCHMAXPATH in size. * *@@changed V0.9.0 [umoeller]: this used to be cmnQuerySystemSound *@@changed V0.9.0 [umoeller]: exported stuff to sndParseSoundData *@@changed V0.9.6 (2000-10-16) [umoeller]: added proper HAB param */ BOOL sndQuerySystemSound(HAB hab, // in: caller's anchor block USHORT usIndex, // in: sound index to query PSZ pszDescr, // out: sound description, as displayed // in the "Sound" object (ptr may be NULL) PSZ pszFile, // out: sound file to (ptr may be NULL) PULONG pulVolume) // out: sound volume (0-100). // If the "Global volume" flag is // set in MMPM.INI, this will return the global // volume instead. Ptr may be NULL also. { BOOL rc = FALSE; HINI hiniMMPM = sndOpenMmpmIni(hab); #ifdef DEBUG_SOUNDS _Pmpf((__FUNCTION__ ": entering, hiniMMPM is 0x%lX", hiniMMPM)); #endif if (hiniMMPM) { CHAR szData[1000]; CHAR szData2[100]; CHAR szKey[10]; sprintf(szKey, "%d", usIndex); PrfQueryProfileString(hiniMMPM, MMINIKEY_SOUNDSETTINGS, "EnableSounds", "TRUE", // default string szData2, sizeof(szData2)); #ifdef DEBUG_SOUNDS _Pmpf((" sounds enabled: %s", szData2)); #endif if (strcmp(szData2, "TRUE") == 0) // sounds enabled at all? if (PrfQueryProfileString(hiniMMPM, MMINIKEY_SYSSOUNDS, szKey, ".", szData, sizeof(szData)-1) > 3) { sndParseSoundData(szData, pszDescr, pszFile, pulVolume); // if "global volume" has been enabled, // we do not return the value specified // here, but the global value PrfQueryProfileString(hiniMMPM, MMINIKEY_SOUNDSETTINGS, "ApplyVolumeToAll", "FALSE", szData2, sizeof(szData2)); if (strcmp(szData2, "FALSE") != 0) { // global volume setting for all sounds PrfQueryProfileString(hiniMMPM, MMINIKEY_SOUNDSETTINGS, "Volume", "100", szData2, sizeof(szData2)); sscanf(szData2, "%lu", pulVolume); } rc = TRUE; } PrfCloseProfile(hiniMMPM); } return rc; } /* *@@ sndWriteSoundData: * this sets a system sound in MMPM.INI. * Gets called by sndSetSystemSound. As opposed * to that function, this needs the profile handle * of MMPM.INI. * * If (pszDescr == NULL), that sound entry is removed. * * Returns the return value of PrfWriteProfileString. * *@@added V0.9.0 [umoeller] *@@changed V0.9.1 (99-12-30) [umoeller]: added delete support *@@changed V0.9.6 (2000-10-16) [umoeller]: added MMPM/2 notify */ BOOL sndWriteSoundData(HINI hiniMMPM, // in: MMPM.INI handle (from sndOpenMmpmIni) USHORT usIndex, // in: sound index PCSZ pszDescr, // in: sound name or NULL for removal PCSZ pszFile, // in: sound file ULONG ulVolume) // in: sound volume { BOOL brc = FALSE; CHAR szKey[10]; sprintf(szKey, "%d", usIndex); if (pszDescr) { CHAR szData[1000]; // format: soundfile#description#volume sprintf(szData, "%s#%s#%lu", pszFile, pszDescr, ulVolume); brc = PrfWriteProfileString(hiniMMPM, MMINIKEY_SYSSOUNDS, szKey, szData); } else // pszDescr == NULL: // delete entry brc = PrfWriteProfileString(hiniMMPM, MMINIKEY_SYSSOUNDS, szKey, NULL); if (brc) // success: if (usIndex < 100) // one of the default OS/2 sounds has changed: // we then need to notify MMPM/2... // this is done by calling WinAlarm with 1000+index! // sick stuff.. WinAlarm(HWND_DESKTOP, WA_FILECHANGED + usIndex); // V0.9.6 (2000-10-16) [umoeller] return brc; } /* *@@ sndSetSystemSound: * this sets a system sound in MMPM.INI by * calling sndWriteSoundData. * Returns FALSE if an error occurred. * * See sndQuerySystemSound for the parameters. * *@@changed V0.9.0 [umoeller]: this used to be cmnSetSystemSound *@@changed V0.9.0 [umoeller]: exported stuff to sndWriteSoundData *@@changed V0.9.6 (2000-10-16) [umoeller]: added proper HAB param */ BOOL sndSetSystemSound(HAB hab, USHORT usIndex, PCSZ pszDescr, PCSZ pszFile, ULONG ulVolume) { BOOL brc = FALSE; HINI hiniMMPM; if (hiniMMPM = sndOpenMmpmIni(hab)) { brc = sndWriteSoundData(hiniMMPM, usIndex, pszDescr, pszFile, ulVolume); PrfCloseProfile(hiniMMPM); } return brc; } /* *@@ sndDoesSchemeExist: * returns TRUE if pszScheme already exists * in OS2SYS.INI. * * If so, and *ppszRealScheme is != NULL, it * is set to the key name found. Since the * scheme names are case-insensitive, this * check is necessary to delete the original * scheme for overwrites. The caller is * responsible for free()ing *ppszRealScheme * then. * *@@added V0.9.0 [umoeller] *@@changed V0.9.20 (2002-07-03) [umoeller]: check has to be case-insensitive, fixed; changed prototype */ BOOL sndDoesSchemeExist(PCSZ pcszScheme, PSZ *ppszRealScheme) // out: actual key name (ptr can be NULL) { BOOL fExists = FALSE; PSZ pszKeysList; if (!prfhQueryKeysForApp(HINI_SYSTEM, MMINIKEY_SOUNDSCHEMES, // "PM_SOUND_SCHEMES_LIST" &pszKeysList)) { PSZ pKey2 = pszKeysList; while (*pKey2) { if (!stricmp(pKey2, pcszScheme)) { fExists = TRUE; if (ppszRealScheme) *ppszRealScheme = strdup(pKey2); break; } pKey2 += strlen(pKey2)+1; // next key } free(pszKeysList); } return fExists; /* old code V0.9.20 (2002-07-03) [umoeller] // check in OS2SYS.INI's scheme list whether that // scheme exists already PSZ pszExisting = prfhQueryProfileData(HINI_SYSTEM, MMINIKEY_SOUNDSCHEMES, // "PM_SOUND_SCHEMES_LIST" pszScheme, NULL); if (pszExisting) { free(pszExisting); return TRUE; } return FALSE; */ } /* *@@ sndCreateSoundScheme: * this creates a new sound scheme and copies * the current data in MMPM.INI into it. * No check is made for whether that sound * scheme exists already (use sndDoesSchemeExist * for that). Data is overwritten without further * discussion. * * Returns: * -- NO_ERROR * -- ERROR_INVALID_HANDLE: hiniMMPM invalid * -- ERROR_NO_DATA: scheme not found. * *@@added V0.9.0 [umoeller] */ APIRET sndCreateSoundScheme(HINI hiniMMPM, // in: MMPM.INI handle (from sndOpenMmpmIni) PCSZ pszNewScheme) // in: name of new scheme { APIRET arc = NO_ERROR; CHAR szNewAppName[200] = "PM_SOUNDS_"; // create a unique new application name for // this scheme in OS2SYS.INI (this is how // Warp 4 apparently does it) strcat(szNewAppName, pszNewScheme); // PM_SOUNDS_blahblah strupr(szNewAppName); // PM_SOUNDS_BLAHBLAH if (hiniMMPM) { // get applications list for sounds list in MMPM.INI PSZ pszKeysList = NULL; if (!(arc = prfhQueryKeysForApp(hiniMMPM, MMINIKEY_SYSSOUNDS, // "MMPM2_AlarmSounds" &pszKeysList))) { PSZ pKey2 = pszKeysList; CHAR szFile[CCHMAXPATH+50]; ULONG ulVolume, ulKeyLen; while (*pKey2 != 0) { // now copy this key to the new sound scheme; // however, we can't just copy the whole key, // but need to extract the file name and // volume first. // Warp 4 normally _only_ stores the file name // in the sound schemes. Since we want the // volume also, we add a null char after the // file name and append the volume... PSZ pSoundData; if (pSoundData = prfhQueryProfileData(hiniMMPM, MMINIKEY_SYSSOUNDS, // "MMPM2_AlarmSounds" pKey2, NULL)) { sndParseSoundData(pSoundData, NULL, // we don't need the description szFile, &ulVolume); ulKeyLen = strlen(szFile)+1; // go beyond null byte ulKeyLen += sprintf(szFile+ulKeyLen, "%lu", ulVolume) + 1; // and write to OS2SYS.INI PrfWriteProfileData(HINI_SYSTEM, szNewAppName, pKey2, szFile, ulKeyLen); free(pSoundData); } // end if (pSoundData) pKey2 += strlen(pKey2)+1; } // end while (*pKey2 != 0) free (pszKeysList); // finally, store new scheme in schemes list PrfWriteProfileString(HINI_SYSTEM, MMINIKEY_SOUNDSCHEMES, // "PM_SOUND_SCHEMES_LIST" (PSZ)pszNewScheme, // key is scheme name szNewAppName); // data is new OS2SYS.INI application } else arc = ERROR_NO_DATA; } else arc = ERROR_INVALID_HANDLE; return arc; } /* *@@ sndLoadSoundScheme: * this loads the data in pszScheme (OS2SYS.INI) * into MMPM.INI. Existing sound data * in that profile will be overwritten. * * Note: Only those sounds in MMPM.INI will be * overwritten for which a corresponding entry * in the sound scheme exists. If it doesn't, * the data in MMPM.INI for _that_ sound only * will _not_ be changed. In other words, existing * sound data will be merged with the scheme data. * * If you don't like this, delete the whole * "MMPM2_AlarmSounds" application in MMPM.INI * before calling this function. * * Returns: * -- NO_ERROR * -- ERROR_INVALID_HANDLE: hiniMMPM invalid * -- ERROR_NO_DATA: scheme not found. * -- ERROR_BAD_FORMAT: error in MMPM.INI data. * *@@added V0.9.0 [umoeller] *@@changed V0.9.1 (99-12-20) [umoeller]: fixed memory leak *@@changed V0.9.6 (2000-10-16) [umoeller]: added MMPM/2 notify */ APIRET sndLoadSoundScheme(HINI hiniMMPM, // in: HINI of ?:\MMOS2\MMPM.INI (PrfOpenProfile) PCSZ pszScheme) // in: scheme name { APIRET arc = NO_ERROR; #ifdef DEBUG_SOUNDS _Pmpf(("Entering sndLoadSoundScheme")); #endif if (hiniMMPM) { // check in OS2SYS.INI's scheme list whether that // scheme exists already PSZ pszSchemeAppName = prfhQueryProfileData(HINI_SYSTEM, MMINIKEY_SOUNDSCHEMES, // "PM_SOUND_SCHEMES_LIST" pszScheme, NULL); #ifdef DEBUG_SOUNDS _Pmpf((" pszSchemeAppName: %s", pszSchemeAppName)); #endif if (pszSchemeAppName) { // now copy keys from OS2SYS.INI to MMPM.INI; // since OS2SYS.INI _only_ has the file name // (and _maybe_ the individual volume, if it // was us who created the sound scheme -- // see sndCreateSoundScheme above), we need // to go thru the MMPM.INI (!) sounds lists // and merge the data in there with the // corresponding sound data for the current // scheme... // get applications list for sounds list in MMPM.INI PSZ pszMMPMKeysList = NULL; if (!(arc = prfhQueryKeysForApp(hiniMMPM, MMINIKEY_SYSSOUNDS,// "MMPM2_AlarmSounds" &pszMMPMKeysList))) { PSZ pMMPMKey2 = pszMMPMKeysList, pMMPMSoundData, pSchemeSoundData; CHAR szDescription[300]; // from MMPM.INI CHAR szFile[CCHMAXPATH+50]; // from OS2SYS.INI ULONG ulVolume; // from MMPM.INI _or_ OS2SYS.INI CHAR szData[1000]; // go thru keys (numbers) while (*pMMPMKey2 != 0) { ULONG cbSchemeSoundData = 0, cbSchemeSoundFile = 0; pMMPMSoundData = prfhQueryProfileData(hiniMMPM, MMINIKEY_SYSSOUNDS, // "MMPM2_AlarmSounds" pMMPMKey2, NULL); pSchemeSoundData = prfhQueryProfileData(HINI_SYSTEM, pszSchemeAppName, pMMPMKey2, &cbSchemeSoundData); if ((pMMPMSoundData) && (pSchemeSoundData)) { sndParseSoundData(pMMPMSoundData, szDescription, NULL, // we don't need the file &ulVolume); // now overwrite this data with scheme data strcpy(szFile, pSchemeSoundData); // up to first null byte cbSchemeSoundFile = strlen(pSchemeSoundData)+1; if (cbSchemeSoundData > cbSchemeSoundFile) // this means we have an additional volume string // after the null byte (sndCreateSoundScheme); // copy it sscanf(pSchemeSoundData + cbSchemeSoundFile, "%lu", &ulVolume); // and write data to MMPM.INI // format: soundfile#description#volume sprintf(szData, "%s#%s#%lu", szFile, szDescription, ulVolume); if (PrfWriteProfileString(hiniMMPM, MMINIKEY_SYSSOUNDS, // "MMPM2_AlarmSounds" pMMPMKey2, // key (decimal number) szData)) { // success: USHORT usIndex = atoi(pMMPMKey2); if (usIndex < 100) // this was one of the default OS/2 sounds: // notify MMPM/2 of the change... see sndWriteSoundData // V0.9.6 (2000-10-16) [umoeller] WinAlarm(HWND_DESKTOP, WA_FILECHANGED + usIndex); } } if (pMMPMSoundData) free(pMMPMSoundData); if (pSchemeSoundData) free(pSchemeSoundData); pMMPMKey2 += strlen(pMMPMKey2)+1; } // end while (*pMMPMKey2 != 0) free (pszMMPMKeysList); } else arc = ERROR_BAD_FORMAT; free(pszSchemeAppName); } // end if (pszSchemeAppName) else arc = ERROR_NO_DATA; } else arc = ERROR_INVALID_HANDLE; #ifdef DEBUG_SOUNDS _Pmpf(("End of sndLoadSoundScheme, arc: %d", arc)); #endif return arc; } /* *@@ sndDestroySoundScheme: * destroys pszScheme in OS2SYS.INI; * returns TRUE is pszScheme was found * and deleted. * * See xsound.c for explanations. * * Returns: * -- NO_ERROR * -- ERROR_NO_DATA: scheme not found. * *@@added V0.9.0 [umoeller] */ APIRET sndDestroySoundScheme(PCSZ pszScheme) { APIRET arc = NO_ERROR; // check in OS2SYS.INI's scheme list whether that // scheme exists PSZ pszExisting; if (pszExisting = prfhQueryProfileData(HINI_SYSTEM, MMINIKEY_SOUNDSCHEMES, // "PM_SOUND_SCHEMES_LIST" pszScheme, NULL)) { // delete whole existing PM_SOUNDS_BLAHBLAH application PrfWriteProfileString(HINI_SYSTEM, pszExisting, // application NULL, NULL); // and delete entry in sound schemes list PrfWriteProfileString(HINI_SYSTEM, MMINIKEY_SOUNDSCHEMES, // "PM_SOUND_SCHEMES_LIST" (PSZ)pszScheme, NULL); free(pszExisting); } else arc = ERROR_NO_DATA; return arc; } /* *@@ fixMmpmIni: * creates the often missing "End drag" * sound item in MMPM.INI if it doesn't exist. * *@@added XWP V1.0.9 (2009-10-08) [pr]: @@fixes 691 */ VOID fixMmpmIni(void) { HINI hiniMmpm; CHAR szMMPM[CCHMAXPATH]; sndQueryMmpmIniPath(szMMPM); if (hiniMmpm = PrfOpenProfile(WinQueryAnchorBlock(HWND_DESKTOP), szMMPM)) { ULONG cbData = 0; CHAR szKey[10]; sprintf (szKey, "%u", MMSOUND_DROP); if ( !PrfQueryProfileSize(hiniMmpm, MMINIKEY_SYSSOUNDS, szKey, &cbData) || (cbData ==0)) { PSZ pszPos; if (pszPos = strrchr (szMMPM, '\\')) { strcpy (pszPos, "\\SOUNDS\\DESKTOP\\DSK_DROP.WAV"); sndWriteSoundData(hiniMmpm, MMSOUND_DROP, "End drag", szMMPM, 40); } } PrfCloseProfile(hiniMmpm); } }