1/* Author: Daniel A. Steffen */
2
3#include "osxMacTcl.h"
4
5#if TARGET_RT_MAC_MACHO
6
7#ifndef __CARBON__
8#include <Carbon/Carbon.h>
9#endif
10
11// ------------------------------------------------------------------------
12
13
14static Tcl_Encoding gFSpPathMacRomanEncoding=NULL;
15
16#define SetupFSpPathEncoding() { \
17	if(!gFSpPathMacRomanEncoding) \
18		gFSpPathMacRomanEncoding = Tcl_GetEncoding(NULL,"macRoman"); \
19}
20
21
22/*============================== CFStrings ==============================*/
23
24/*
25 * CFStringGetCString[Path]() with kCFStringEncodingUTF8 do not work.
26 * They return fully decomposed Utf8 characters which Tcl does not
27 * understand.  See Bug 587 and associated discussion on
28 * AlphaTcl-developers
29 *
30 * !!!  conversion through macRoman is grotesque and should not be
31 * necessary, but Tcl appears not to properly handle accented character
32 * encodings.  See Bug 587 and the associated discussion on
33 * AlphaTcl-developers. !!!
34 */
35
36#ifndef MAC_OS_X_VERSION_10_2
37/* define constants from 10.2 CFString.h to allow compilation in 10.1 */
38typedef enum {
39	kCFStringNormalizationFormD = 0, // Canonical Decomposition
40	kCFStringNormalizationFormKD, // Compatibility Decomposition
41	kCFStringNormalizationFormC, // Canonical Decomposition followed by Canonical Composition
42	kCFStringNormalizationFormKC // Compatibility Decomposition followed by Canonical Composition
43} CFStringNormalizationForm;
44#endif
45
46/*
47 *----------------------------------------------------------------------
48 *
49 * TryCFStringNormalize --
50 *
51 * call the 10.2 only CFStringNormalize() in a backwards compatible way.
52 *
53 * Results:
54 *	normalized mutable copy of string (retained, needs to be released!)
55 *  or NULL if CFStringNormalize not available.
56 *
57 * Side effects:
58 *	None.
59 *
60 *----------------------------------------------------------------------
61 */
62#include <mach-o/dyld.h>
63
64static CFMutableStringRef TryCFStringNormalize(CFStringRef theString, CFStringNormalizationForm theForm)
65{
66	static Boolean initialized = FALSE;
67	static void (*cfstringnormalize)(CFMutableStringRef, CFStringNormalizationForm) = NULL;
68
69	if(!initialized) {
70		if(NSIsSymbolNameDefinedWithHint("_CFStringNormalize", "CoreFoundation")) {
71			NSSymbol nsSymbol = NSLookupAndBindSymbolWithHint("_CFStringNormalize", "CoreFoundation");
72			if(nsSymbol)  cfstringnormalize = NSAddressOfSymbol(nsSymbol);
73		}
74		initialized = TRUE;
75	}
76	if(cfstringnormalize) {
77		CFMutableStringRef theMutableString = CFStringCreateMutableCopy(NULL, 0, theString);
78		if (theMutableString) {
79			cfstringnormalize(theMutableString, theForm);
80			return(theMutableString);
81		}
82	}
83	return(NULL);
84}
85
86/*
87 *----------------------------------------------------------------------
88 *
89 * CFStringToDString --
90 *
91 *	This helper function converts a CFString into a DString,
92 *  first transforming to canonical composed or decomposed unicode
93 *  depending on the 'compose' flag then transforming to external
94 *  (macRoman) encoding if 'external' is set.
95 *
96 *  Uses the most direct transformation possible on the current
97 *  system, e.g. CFStringNormalize if available, or by transcoding
98 *  to/from macRoman otherwise (potentially lossy!).
99 *
100 * Results:
101 *	Tcl error code.
102 *
103 * Side effects:
104 *	None.
105 *
106 *----------------------------------------------------------------------
107 */
108
109static int CFStringToDString(Tcl_Interp * interp, CFStringRef strRef, Tcl_DString * dsPtr,
110			Boolean compose, Boolean external)
111{
112	Boolean			success;
113	int				len;
114	int				result = TCL_ERROR;
115
116	CFStringRef 		theStrRef = NULL;
117	CFStringEncoding 	theEncoding;
118	Tcl_DString			ds, *theDsPtr = dsPtr;
119	Boolean				usedNormalize = FALSE;
120
121	if (compose)
122		theStrRef = TryCFStringNormalize(strRef, kCFStringNormalizationFormC);
123	else
124		theStrRef = TryCFStringNormalize(strRef, kCFStringNormalizationFormD);
125
126	if(theStrRef) {
127		usedNormalize = TRUE;
128
129	} else {
130		theStrRef = strRef;
131		theEncoding = kCFStringEncodingMacRoman;
132	}
133
134	if (usedNormalize && !external)
135		theEncoding = kCFStringEncodingUTF8;
136	else
137		theEncoding = kCFStringEncodingMacRoman;
138
139	if(!usedNormalize && !external)
140		theDsPtr = &ds; // will need ExternalToUtf conversion
141
142	len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(theStrRef), theEncoding);
143	Tcl_DStringInit(theDsPtr);
144	Tcl_DStringSetLength(theDsPtr, len);
145
146	success = CFStringGetCString(theStrRef, Tcl_DStringValue(theDsPtr), len+1, theEncoding);
147
148	if (success) {
149		/* len was only a guess */
150		Tcl_DStringSetLength(theDsPtr, strlen(Tcl_DStringValue(theDsPtr)));
151		result = TCL_OK;
152	} else
153		if (interp)  Tcl_SetResult(interp, "Can't extract string from CFString", TCL_STATIC);
154
155	if (!usedNormalize && !external) {
156		// need ExternalToUtf conversion
157		if(success) {
158			SetupFSpPathEncoding();
159			Tcl_ExternalToUtfDString(gFSpPathMacRomanEncoding,
160					Tcl_DStringValue(theDsPtr), Tcl_DStringLength(theDsPtr), dsPtr);
161		}
162		Tcl_DStringFree(theDsPtr);
163	}
164	if(usedNormalize)
165		CFRelease(theStrRef);
166
167	return result;
168}
169
170/*
171 *----------------------------------------------------------------------
172 *
173 * BufferToDString --
174 *
175 *	This helper function converts a text buffer of given lenth into
176 *  a DString (if length == -1, buffer is assumed to be a C string),
177 *  first transforming from external (macRoman) encoding if
178 *  'fromExternal' is set, then transforming to canonical composed
179 *  or decomposed unicode depending on the 'compose' flag and finally
180 *  transforming to external (macRoman) encoding if 'external' is set.
181 *
182 *  Tries to use the most efficient transformations possible on the
183 *  current  system, e.g. CFStringNormalize if available, and
184 *  CFStringCreateWithCStringNoCopy if given a C string.
185 *
186 * Results:
187 *	Tcl error code.
188 *
189 * Side effects:
190 *	None.
191 *
192 *----------------------------------------------------------------------
193 */
194
195static int BufferToDString(Tcl_Interp * interp, CONST84 char *buffer, int length,
196		Tcl_DString * dsPtr, Boolean compose, Boolean toExternal, Boolean fromExternal)
197{
198	int result = TCL_ERROR;
199	CFStringRef			theString;
200	CFStringEncoding 	theEncoding;
201
202	theEncoding = (fromExternal ? kCFStringEncodingMacRoman : kCFStringEncodingUTF8);
203
204	if(length < 0) //assume buffer is a C string
205	    theString = CFStringCreateWithCStringNoCopy(NULL, buffer, theEncoding, kCFAllocatorNull);
206	else
207	    theString = CFStringCreateWithBytes(NULL, (const unsigned char *) buffer, length, theEncoding, FALSE);
208
209	if(theString) {
210		result = CFStringToDString(interp, theString, dsPtr, compose, toExternal);
211		CFRelease(theString); // bug 671
212	} else {
213		if (interp)  Tcl_SetResult(interp, "Can't create CFString from buffer", TCL_STATIC);
214	}
215
216	return result;
217}
218
219/* CFString to external DString */
220int CFStringToExternalDString(Tcl_Interp * interp, CFStringRef strRef, Tcl_DString * dsPtr)
221{
222	return CFStringToDString(interp, strRef, dsPtr, TRUE, TRUE);
223}
224
225/* CFString to DString */
226int CFStringToUtfDString(Tcl_Interp * interp, CFStringRef strRef, Tcl_DString * dsPtr)
227{
228	return CFStringToDString(interp, strRef, dsPtr, TRUE, FALSE);
229}
230
231/* decomposed utf8 buffer to external DString */
232int DUtfToExternalDString(Tcl_Interp * interp, CONST84 char * src, int length, Tcl_DString * dsPtr)
233{
234	return BufferToDString(interp, src, length, dsPtr, TRUE, TRUE, FALSE);
235}
236
237/* decomposed utf8 buffer to DString */
238int DUtfToUtfDString(Tcl_Interp * interp, CONST84 char * src, int length, Tcl_DString * dsPtr)
239{
240	return BufferToDString(interp, src, length, dsPtr, TRUE, FALSE, FALSE);
241}
242
243/* external buffer to decomposed utf8 DString */
244int ExternalToDUtfDString(Tcl_Interp * interp, CONST84 char * src, int length, Tcl_DString * dsPtr)
245{
246	return BufferToDString(interp, src, length, dsPtr, FALSE, FALSE, TRUE);
247}
248
249/* utf8 buffer to decomposed utf8 DString */
250int UtfToDUtfDString(Tcl_Interp * interp, CONST84 char * src, int length, Tcl_DString * dsPtr)
251{
252	return BufferToDString(interp, src, length, dsPtr, FALSE, FALSE, FALSE);
253}
254
255/*==============================        ==============================*/
256
257//  das 091200 reimplemented the following routines from scratch
258//             for Tcl on OSX using modern FileMgr APIs and FSRefs
259//             they are analogous to the MacTcl routines in tclMacUtil.c
260//
261//             on OSX these routines are used in oldEndre.c instead
262//             of the crufty old Alpha versions
263
264
265#define MAXPATHLEN 1024
266
267/*
268 *----------------------------------------------------------------------
269 *
270 * FSpLocationFromPath --
271 *
272 *	This function obtains an FSSpec for a given macintosh path.
273 *	Unlike the More Files function FSpLocationFromFullPath, this
274 *	function will also accept partial paths and resolve any aliases
275 *	along the path. It will also create an FSSpec for a path that
276 *	does not yet exist.
277 *
278 * Results:
279 *	OSErr code.
280 *
281 * Side effects:
282 *	None.
283 *
284 *----------------------------------------------------------------------
285 */
286
287
288OSErr
289FSpLocationFromPath(
290    int length,			/* Length of path. */
291    CONST84 char *path,		/* The path to convert. */
292    FSSpecPtr fileSpecPtr)	/* On return the spec for the path. */
293{
294    UInt8 fileName[MAXPATHLEN];
295	unsigned int fileNameLen;
296    OSErr err;
297    unsigned int pos, cur = 0;
298    Boolean isDirectory=TRUE, filenotexist=FALSE, wasAlias, done;
299    FSRef fsref;
300	Tcl_DString ds;
301
302	// FSRefMakePath et al use deomposed UTF8 on OSX
303	if(ExternalToDUtfDString(NULL, path, length, &ds) == TCL_ERROR) {
304		err = coreFoundationUnknownErr;
305		goto done;
306	}
307
308	path = Tcl_DStringValue(&ds);
309	length = Tcl_DStringLength(&ds);
310
311    pos = 0;
312    fileName[0] = 0;
313	fileNameLen = 0;
314
315    /*
316     * Check to see if this is a full path.  If partial
317     * we assume that path starts with the current working
318     * directory.  (Ie. volume & dir = 0)
319     */
320    if ((done=(length == 0)) || (path[0] != '/')) {
321	     // start at current directory
322		{
323		CFBundleRef appBundle=CFBundleGetMainBundle();
324		CFURLRef	appURL=NULL, parentURL=NULL;
325		err = coreFoundationUnknownErr;
326		if(appBundle)
327		{
328			appURL=CFBundleCopyBundleURL(appBundle);
329			if(appURL)
330			{
331				parentURL=CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault,appURL);
332				CFRelease(appURL);
333				if(parentURL)
334				{
335					if(CFURLGetFSRef(parentURL, &fsref))
336						err=noErr;
337					CFRelease(parentURL);
338				}
339			}
340		}
341		}
342		if (err != noErr) goto done;
343		if(!done){
344			err = FSRefMakePath(&fsref,fileName,MAXPATHLEN);
345			if (err != noErr) goto done;
346			fileNameLen=strlen( (const char *) fileName);
347		}
348    } else {
349    if(path[0] == '/') {
350        if((done=(length == 1)))
351        {
352	    /*
353	     * If path = "/", just get root directory.
354	     */
355            err = FSPathMakeRef((UInt8 *) path, &fsref, &isDirectory);
356            if (err != noErr) goto done;
357        } else {
358            pos++;
359        }
360
361    }
362    }
363    if(!done) {
364        fileName[fileNameLen++] = '/';
365        fileName[fileNameLen] = 0;
366
367    while (pos < length) {
368	if (!isDirectory || filenotexist) {err=dirNFErr; goto done;}
369	cur = pos;
370	while (path[pos] != '/' && pos < length) {
371	    pos++;
372	}
373	if (fileNameLen+pos-cur > MAXPATHLEN) {
374	    err=bdNamErr; goto done;
375	} else {
376	    strncpy( (char *) fileName+fileNameLen, &path[cur], pos - cur);
377	    fileNameLen += pos - cur;
378	}
379        fileName[fileNameLen] = 0;
380        err = FSPathMakeRef(fileName, &fsref, &isDirectory);
381        if ((err != noErr) && !(filenotexist=(err == fnfErr))) goto done;
382        if (!filenotexist) {
383			err = FSResolveAliasFile(&fsref, true, &isDirectory, &wasAlias);
384			if (err != noErr) goto done;
385			if(wasAlias){
386				err = FSRefMakePath(&fsref,fileName,MAXPATHLEN);
387            if (err != noErr) goto done;
388			fileNameLen=strlen( (const char *) fileName);
389			}
390        }
391
392	if (path[pos] == '/') {
393            if (!isDirectory || filenotexist) {err=dirNFErr; goto done;}
394	    pos++;
395	    fileName[fileNameLen++] = '/';
396		fileName[fileNameLen] = 0;
397	}
398    }
399    }
400    if(!filenotexist) {
401        err = FSGetCatalogInfo(&fsref,kFSCatInfoNone,NULL,NULL,fileSpecPtr,NULL);
402    } else {
403        FSCatalogInfo catalogInfo;
404		Tcl_DString ds;
405        if(pos - cur>sizeof(StrFileName)) {err=bdNamErr; goto done;}
406        err = FSGetCatalogInfo(&fsref, kFSCatInfoNodeID | kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
407        if (err != noErr) goto done;
408		fileSpecPtr->vRefNum = catalogInfo.volume;
409        fileSpecPtr->parID	 = catalogInfo.nodeID;
410		if(DUtfToExternalDString(NULL, &path[cur], pos - cur, &ds) == TCL_ERROR) {
411			err = coreFoundationUnknownErr;
412			goto done;
413		}
414        strncpy( (char *) fileSpecPtr->name+1, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
415        fileSpecPtr->name[0] = Tcl_DStringLength(&ds);
416		Tcl_DStringFree(&ds);
417		err = fnfErr;
418    }
419
420done:
421	Tcl_DStringFree(&ds);
422    return err;
423}
424
425/*
426 *----------------------------------------------------------------------
427 *
428 * FSpPathFromLocation --
429 *
430 *	This function obtains a full path name for a given macintosh
431 *	FSSpec.  Unlike the More Files function FSpGetFullPath, this
432 *	function will return a C string in the Handle.  It also will
433 *	create paths for FSSpec that do not yet exist.
434 *
435 * Results:
436 *	OSErr code.
437 *
438 * Side effects:
439 *	None.
440 *
441 *----------------------------------------------------------------------
442 */
443
444OSErr
445FSpPathFromLocation(
446    FSSpecPtr spec,		/* The location we want a path for. */
447    int *length,		/* Length of the resulting path. */
448    Handle *fullPath)		/* Handle to path. */
449{
450    OSErr err;
451    FSSpec tempSpec;
452    UInt8 fileName[MAXPATHLEN];
453	unsigned int fileNameLen;
454    Boolean filenotexist=FALSE;
455    FSRef fsref;
456
457    *fullPath = NULL;
458
459    err=FSpMakeFSRef(spec,&fsref);
460
461    if ((err == noErr) || (filenotexist=(err == fnfErr))) {
462        if (filenotexist) {
463            // file does not exist, find parent dir
464            memmove(&tempSpec, spec, sizeof(FSSpec));
465            tempSpec.name[0]=0;
466            err=FSpMakeFSRef(&tempSpec,&fsref);
467        }
468        if (err == noErr) {
469            err = FSRefMakePath(&fsref,fileName,MAXPATHLEN);
470            if (err == noErr) {
471                fileNameLen=strlen( (const char *) fileName);
472                if(filenotexist) { // add file name manually
473                    // need to convert spec name from macroman to utf8d before adding to fileName
474                    Tcl_DString ds;
475                    if (ExternalToDUtfDString(NULL, (char *) &spec->name[1], spec->name[0], &ds) == TCL_OK) { // bug 671
476                        if(fileNameLen+Tcl_DStringLength(&ds)<MAXPATHLEN) {
477                            fileName[fileNameLen++] = '/';
478                            strncpy( (char *) fileName+fileNameLen, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
479                            fileNameLen += Tcl_DStringLength(&ds);
480                        } else {
481                            err=bdNamErr;
482                        }
483                        Tcl_DStringFree(&ds);
484                    } else {
485                        err = coreFoundationUnknownErr;
486                    }
487                } else {
488                    FSCatalogInfo catalogInfo;
489                    err = FSGetCatalogInfo(&fsref,kFSCatInfoNodeFlags,&catalogInfo,NULL,NULL,NULL);
490                    if (err == noErr && (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) {
491                        // if we have a directory, end path with /
492                        if(fileNameLen < MAXPATHLEN) {
493                            fileName[fileNameLen++] = '/';
494                        } else {
495                            err=bdNamErr;
496                        }
497                    }
498                }
499                if (err == noErr) {
500                    // FSRefMakePath et al use decomposed UTF8 on OSX
501                    Tcl_DString ds;
502                    fileName[fileNameLen] = 0; // add 0 cstr terminator
503                    if (DUtfToExternalDString(NULL, (const char *) fileName, -1, &ds) == TCL_OK) {
504                        err = PtrToHand(Tcl_DStringValue(&ds), fullPath, Tcl_DStringLength(&ds)+1);
505                        *length = Tcl_DStringLength(&ds);
506                        Tcl_DStringFree(&ds); // bug 671
507                    } else {
508                        err = coreFoundationUnknownErr;
509                    }
510                }
511            }
512        }
513    }
514
515    /*
516     * On error Dispose the handle, set it to NULL & return the err.
517     * Otherwise, set the length & return.
518     */
519    if (err != noErr) {
520        if ( *fullPath != NULL ) {
521            DisposeHandle(*fullPath);
522        }
523        *fullPath = NULL;
524        *length = 0;
525    }
526
527    return err;
528}
529
530// ------------------------------------------------------------------------
531
532
533//  das 271100 modified and adapted from MacTcl for Tcl on OSX
534
535
536/*
537 * tclMacResource.c --
538 *
539 *	This file contains several commands that manipulate or use
540 *	Macintosh resources.  Included are extensions to the "source"
541 *	command, the mac specific "beep" and "resource" commands, and
542 *	administration for open resource file references.
543 *
544 * Copyright (c) 1996-1997 Sun Microsystems, Inc.
545 *
546 * See the file "license.terms" for information on usage and redistribution
547 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
548 *
549 * RCS: @(#) $Id: osxMacTcl.c,v 1.2 2007/08/23 10:58:27 das Exp $
550 */
551
552/*
553 * Pass this in the mode parameter of SetSoundVolume to determine
554 * which volume to set.
555 */
556
557enum WhichVolume {
558    SYS_BEEP_VOLUME,    /* This sets the volume for SysBeep calls */
559    DEFAULT_SND_VOLUME, /* This one for SndPlay calls */
560    RESET_VOLUME        /* And this undoes the last call to SetSoundVolume */
561};
562
563/*
564 * Prototypes for procedures defined later in this file:
565 */
566
567
568static void 		SetSoundVolume(int volume, enum WhichVolume mode);
569
570/*
571 *----------------------------------------------------------------------
572 *
573 * Tcl_BeepObjCmd --
574 *
575 *	This procedure makes the beep sound.
576 *
577 * Results:
578 *	A standard Tcl result.
579 *
580 * Side effects:
581 *	Makes a beep.
582 *
583 *----------------------------------------------------------------------
584 */
585
586int
587Tcl_BeepObjCmd(
588    ClientData dummy,			/* Not used. */
589    Tcl_Interp *interp,			/* Current interpreter. */
590    int objc,				/* Number of arguments. */
591    Tcl_Obj *CONST84 objv[])		/* Argument values. */
592{
593    Tcl_Obj *resultPtr, *objPtr;
594    Handle sound;
595    Str255 sndName;
596    int volume = -1, length;
597    char * sndArg = NULL;
598
599    resultPtr = Tcl_GetObjResult(interp);
600    if (objc == 1) {
601	SysBeep(1);
602	return TCL_OK;
603    } else if (objc == 2) {
604	if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-list")) {
605	    int count, i;
606	    short id;
607	    Str255 theName;
608	    ResType rezType;
609
610	    count = CountResources('snd ');
611	    for (i = 1; i <= count; i++) {
612		sound = GetIndResource('snd ', i);
613		if (sound != NULL) {
614		    GetResInfo(sound, &id, &rezType, theName);
615		    if (theName[0] == 0) {
616			continue;
617		    }
618		    objPtr = Tcl_NewStringObj((char *) theName + 1,
619			    theName[0]);
620		    Tcl_ListObjAppendElement(interp, resultPtr, objPtr);
621		}
622	    }
623	    return TCL_OK;
624	} else {
625	    sndArg = Tcl_GetStringFromObj(objv[1], &length);
626	}
627    } else if (objc == 3) {
628	if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-volume")) {
629	    Tcl_GetIntFromObj(interp, objv[2], &volume);
630	} else {
631	    goto beepUsage;
632	}
633    } else if (objc == 4) {
634	if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-volume")) {
635	    Tcl_GetIntFromObj(interp, objv[2], &volume);
636	    sndArg = Tcl_GetStringFromObj(objv[3], &length);
637	} else {
638	    goto beepUsage;
639	}
640    } else {
641	goto beepUsage;
642    }
643
644    /*
645     * Play the sound
646     */
647    if (sndArg == NULL) {
648	/*
649         * Set Volume for SysBeep
650         */
651
652	if (volume >= 0) {
653	    SetSoundVolume(volume, SYS_BEEP_VOLUME);
654	}
655	SysBeep(1);
656
657	/*
658         * Reset Volume
659         */
660
661	if (volume >= 0) {
662	    SetSoundVolume(0, RESET_VOLUME);
663	}
664    } else {
665	strcpy((char *) sndName + 1, sndArg);
666	sndName[0] = length;
667	sound = GetNamedResource('snd ', sndName);
668	if (sound != NULL) {
669	    /*
670             * Set Volume for Default Output device
671             */
672
673	    if (volume >= 0) {
674		SetSoundVolume(volume, DEFAULT_SND_VOLUME);
675	    }
676
677	    SndPlay(NULL, (SndListHandle) sound, false);
678
679	    /*
680             * Reset Volume
681             */
682
683	    if (volume >= 0) {
684		SetSoundVolume(0, RESET_VOLUME);
685	    }
686	} else {
687	    Tcl_AppendStringsToObj(resultPtr, " \"", sndArg,
688		    "\" is not a valid sound.  (Try ",
689		    Tcl_GetString(objv[0]), " -list)", NULL);
690	    return TCL_ERROR;
691	}
692    }
693
694    return TCL_OK;
695
696    beepUsage:
697    Tcl_WrongNumArgs(interp, 1, objv, "[-volume num] [-list | sndName]?");
698    return TCL_ERROR;
699}
700
701/*
702 *-----------------------------------------------------------------------------
703 *
704 * SetSoundVolume --
705 *
706 *	Set the volume for either the SysBeep or the SndPlay call depending
707 *	on the value of mode (SYS_BEEP_VOLUME or DEFAULT_SND_VOLUME
708 *      respectively.
709 *
710 *      It also stores the last channel set, and the old value of its
711 *	VOLUME.  If you call SetSoundVolume with a mode of RESET_VOLUME,
712 *	it will undo the last setting.  The volume parameter is
713 *      ignored in this case.
714 *
715 * Side Effects:
716 *	Sets the System Volume
717 *
718 * Results:
719 *      None
720 *
721 *-----------------------------------------------------------------------------
722 */
723
724void
725SetSoundVolume(
726    int volume,              /* This is the new volume */
727    enum WhichVolume mode)   /* This flag says which volume to
728			      * set: SysBeep, SndPlay, or instructs us
729			      * to reset the volume */
730{
731    static int hasSM3 = 1;
732    static enum WhichVolume oldMode;
733    static long oldVolume = -1;
734
735
736    /*
737     * If we don't have Sound Manager 3.0, we can't set the sound volume.
738     * We will just ignore the request rather than raising an error.
739     */
740
741    if (!hasSM3) {
742    	return;
743    }
744
745    switch (mode) {
746    	case SYS_BEEP_VOLUME:
747	    GetSysBeepVolume(&oldVolume);
748	    SetSysBeepVolume(volume);
749	    oldMode = SYS_BEEP_VOLUME;
750	    break;
751	case DEFAULT_SND_VOLUME:
752	    GetDefaultOutputVolume(&oldVolume);
753	    SetDefaultOutputVolume(volume);
754	    oldMode = DEFAULT_SND_VOLUME;
755	    break;
756	case RESET_VOLUME:
757	    /*
758	     * If oldVolume is -1 someone has made a programming error
759	     * and called reset before setting the volume.  This is benign
760	     * however, so we will just exit.
761	     */
762
763	    if (oldVolume != -1) {
764	        if (oldMode == SYS_BEEP_VOLUME) {
765	    	    SetSysBeepVolume(oldVolume);
766	        } else if (oldMode == DEFAULT_SND_VOLUME) {
767		    SetDefaultOutputVolume(oldVolume);
768	        }
769	    }
770	    oldVolume = -1;
771    }
772}
773
774
775#endif
776