1#include "QuickTimeTcl.h"
2#include "osxMacTcl.h"
3
4//#if TARGET_RT_MAC_MACHO
5
6#ifndef __CARBON__
7#include <Carbon/Carbon.h>
8#endif
9
10// ------------------------------------------------------------------------
11
12
13static Tcl_Encoding gFSpPathMacRomanEncoding=NULL;
14
15#define SetupFSpPathEncoding() { \
16	if(!gFSpPathMacRomanEncoding) \
17		gFSpPathMacRomanEncoding = Tcl_GetEncoding(NULL,"macRoman"); \
18}
19
20
21/*============================== CFStrings ==============================*/
22
23/*
24 * CFStringGetCString[Path]() with kCFStringEncodingUTF8 do not work.
25 * They return fully decomposed Utf8 characters which Tcl does not
26 * understand.  See Bug 587 and associated discussion on
27 * AlphaTcl-developers
28 *
29 * !!!  conversion through macRoman is grotesque and should not be
30 * necessary, but Tcl appears not to properly handle accented character
31 * encodings.  See Bug 587 and the associated discussion on
32 * AlphaTcl-developers. !!!
33 */
34
35#ifndef MAC_OS_X_VERSION_10_2
36/* define constants from 10.2 CFString.h to allow compilation in 10.1 */
37typedef enum {
38	kCFStringNormalizationFormD = 0, // Canonical Decomposition
39	kCFStringNormalizationFormKD, // Compatibility Decomposition
40	kCFStringNormalizationFormC, // Canonical Decomposition followed by Canonical Composition
41	kCFStringNormalizationFormKC // Compatibility Decomposition followed by Canonical Composition
42} CFStringNormalizationForm;
43#endif
44
45/*
46 *----------------------------------------------------------------------
47 *
48 * TryCFStringNormalize --
49 *
50 * call the 10.2 only CFStringNormalize() in a backwards compatible way.
51 *
52 * Results:
53 *	normalized mutable copy of string (retained, needs to be released!)
54 *  or NULL if CFStringNormalize not available.
55 *
56 * Side effects:
57 *	None.
58 *
59 *----------------------------------------------------------------------
60 */
61#include <mach-o/dyld.h>
62
63static CFMutableStringRef TryCFStringNormalize(CFStringRef theString, CFStringNormalizationForm theForm)
64{
65	static Boolean initialized = FALSE;
66	static void (*cfstringnormalize)(CFMutableStringRef, CFStringNormalizationForm) = NULL;
67
68	if(!initialized) {
69		if(NSIsSymbolNameDefinedWithHint("_CFStringNormalize", "CoreFoundation")) {
70			NSSymbol nsSymbol = NSLookupAndBindSymbolWithHint("_CFStringNormalize", "CoreFoundation");
71			if(nsSymbol)  cfstringnormalize = NSAddressOfSymbol(nsSymbol);
72		}
73		initialized = TRUE;
74	}
75	if(cfstringnormalize) {
76		CFMutableStringRef theMutableString = CFStringCreateMutableCopy(NULL, 0, theString);
77		if (theMutableString) {
78			cfstringnormalize(theMutableString, theForm);
79			return(theMutableString);
80		}
81	}
82	return(NULL);
83}
84
85/*
86 *----------------------------------------------------------------------
87 *
88 * CFStringToDString --
89 *
90 *	This helper function converts a CFString into a DString,
91 *  first transforming to canonical composed or decomposed unicode
92 *  depending on the 'compose' flag then transforming to external
93 *  (macRoman) encoding if 'external' is set.
94 *
95 *  Uses the most direct transformation possible on the current
96 *  system, e.g. CFStringNormalize if available, or by transcoding
97 *  to/from macRoman otherwise (potentially lossy!).
98 *
99 * Results:
100 *	Tcl error code.
101 *
102 * Side effects:
103 *	None.
104 *
105 *----------------------------------------------------------------------
106 */
107
108static int CFStringToDString(Tcl_Interp * interp, CFStringRef strRef, Tcl_DString * dsPtr,
109			Boolean compose, Boolean external)
110{
111	CONST84 char *	str;
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, needConvert;
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;
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, 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;
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		CFURLPathStyle pathStyle=kCFURLPOSIXPathStyle;
326		err = coreFoundationUnknownErr;
327		if(appBundle)
328		{
329			appURL=CFBundleCopyBundleURL(appBundle);
330			if(appURL)
331			{
332				parentURL=CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault,appURL);
333				CFRelease(appURL);
334				if(parentURL)
335				{
336					if(CFURLGetFSRef(parentURL, &fsref))
337						err=noErr;
338					CFRelease(parentURL);
339				}
340			}
341		}
342		}
343		if (err != noErr) goto done;
344		if(!done){
345			err = FSRefMakePath(&fsref,fileName,MAXPATHLEN);
346			if (err != noErr) goto done;
347			fileNameLen=strlen(fileName);
348		}
349    } else {
350    if(path[0] == '/') {
351        if((done=(length == 1)))
352        {
353	    /*
354	     * If path = "/", just get root directory.
355	     */
356            err = FSPathMakeRef((UInt8 *) path, &fsref, &isDirectory);
357            if (err != noErr) goto done;
358        } else {
359            pos++;
360        }
361
362    }
363    }
364    if(!done) {
365        fileName[fileNameLen++] = '/';
366        fileName[fileNameLen] = 0;
367
368    while (pos < length) {
369	if (!isDirectory || filenotexist) {err=dirNFErr; goto done;}
370	cur = pos;
371	while (path[pos] != '/' && pos < length) {
372	    pos++;
373	}
374	if (fileNameLen+pos-cur > MAXPATHLEN) {
375	    err=bdNamErr; goto done;
376	} else {
377	    strncpy(fileName+fileNameLen, &path[cur], pos - cur);
378	    fileNameLen += pos - cur;
379	}
380        fileName[fileNameLen] = 0;
381        err = FSPathMakeRef(fileName, &fsref, &isDirectory);
382        if ((err != noErr) && !(filenotexist=(err == fnfErr))) goto done;
383        if (!filenotexist) {
384			err = FSResolveAliasFile(&fsref, true, &isDirectory, &wasAlias);
385			if (err != noErr) goto done;
386			if(wasAlias){
387				err = FSRefMakePath(&fsref,fileName,MAXPATHLEN);
388            if (err != noErr) goto done;
389			fileNameLen=strlen(fileName);
390			}
391        }
392
393	if (path[pos] == '/') {
394            if (!isDirectory || filenotexist) {err=dirNFErr; goto done;}
395	    pos++;
396	    fileName[fileNameLen++] = '/';
397		fileName[fileNameLen] = 0;
398	}
399    }
400    }
401    if(!filenotexist) {
402        err = FSGetCatalogInfo(&fsref,kFSCatInfoNone,NULL,NULL,fileSpecPtr,NULL);
403    } else {
404        FSCatalogInfo catalogInfo;
405		Tcl_DString ds;
406        if(pos - cur>sizeof(StrFileName)) {err=bdNamErr; goto done;}
407        err = FSGetCatalogInfo(&fsref, kFSCatInfoNodeID | kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
408        if (err != noErr) goto done;
409		fileSpecPtr->vRefNum = catalogInfo.volume;
410        fileSpecPtr->parID	 = catalogInfo.nodeID;
411		if(DUtfToExternalDString(NULL, &path[cur], pos - cur, &ds) == TCL_ERROR) {
412			err = coreFoundationUnknownErr;
413			goto done;
414		}
415        strncpy(fileSpecPtr->name+1, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
416        fileSpecPtr->name[0] = Tcl_DStringLength(&ds);
417		Tcl_DStringFree(&ds);
418        err = fnfErr;
419    }
420
421done:
422	Tcl_DStringFree(&ds);
423    return err;
424}
425
426/*
427 *----------------------------------------------------------------------
428 *
429 * FSpPathFromLocation --
430 *
431 *	This function obtains a full path name for a given macintosh
432 *	FSSpec.  Unlike the More Files function FSpGetFullPath, this
433 *	function will return a C string in the Handle.  It also will
434 *	create paths for FSSpec that do not yet exist.
435 *
436 * Results:
437 *	OSErr code.
438 *
439 * Side effects:
440 *	None.
441 *
442 *----------------------------------------------------------------------
443 */
444
445OSErr
446FSpPathFromLocation(
447    FSSpecPtr spec,		/* The location we want a path for. */
448    int *length,		/* Length of the resulting path. */
449    Handle *fullPath)		/* Handle to path. */
450{
451    OSErr err;
452    FSSpec tempSpec;
453    UInt8 fileName[MAXPATHLEN];
454	unsigned int fileNameLen;
455    Boolean filenotexist=FALSE;
456    FSRef fsref;
457
458    *fullPath = NULL;
459
460    err=FSpMakeFSRef(spec,&fsref);
461
462    if ((err == noErr) || (filenotexist=(err == fnfErr))) {
463        if (filenotexist) {
464            // file does not exist, find parent dir
465            memmove(&tempSpec, spec, sizeof(FSSpec));
466            tempSpec.name[0]=0;
467            err=FSpMakeFSRef(&tempSpec,&fsref);
468        }
469        if (err == noErr) {
470            err = FSRefMakePath(&fsref,fileName,MAXPATHLEN);
471            if (err == noErr) {
472                fileNameLen=strlen(fileName);
473                if(filenotexist) { // add file name manually
474                    // need to convert spec name from macroman to utf8d before adding to fileName
475                    Tcl_DString ds;
476                    if (ExternalToDUtfDString(NULL,&spec->name[1], spec->name[0], &ds) == TCL_OK) { // bug 671
477                        if(fileNameLen+Tcl_DStringLength(&ds)<MAXPATHLEN) {
478                            fileName[fileNameLen++] = '/';
479                            strncpy(fileName+fileNameLen, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
480                            fileNameLen += Tcl_DStringLength(&ds);
481                        } else {
482                            err=bdNamErr;
483                        }
484                        Tcl_DStringFree(&ds);
485                    } else {
486                        err = coreFoundationUnknownErr;
487                    }
488                } else {
489                    FSCatalogInfo catalogInfo;
490                    err = FSGetCatalogInfo(&fsref,kFSCatInfoNodeFlags,&catalogInfo,NULL,NULL,NULL);
491                    if (err == noErr && (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) {
492                        // if we have a directory, end path with /
493                        if(fileNameLen < MAXPATHLEN) {
494                            fileName[fileNameLen++] = '/';
495                        } else {
496                            err=bdNamErr;
497                        }
498                    }
499                }
500                if (err == noErr) {
501                    // FSRefMakePath et al use decomposed UTF8 on OSX
502                    Tcl_DString ds;
503                    fileName[fileNameLen] = 0; // add 0 cstr terminator
504                    if (DUtfToExternalDString(NULL, fileName, -1, &ds) == TCL_OK) {
505                        err = PtrToHand(Tcl_DStringValue(&ds), fullPath, Tcl_DStringLength(&ds)+1);
506                        *length = Tcl_DStringLength(&ds);
507                        Tcl_DStringFree(&ds); // bug 671
508                    } else {
509                        err = coreFoundationUnknownErr;
510                    }
511                }
512            }
513        }
514    }
515
516    /*
517     * On error Dispose the handle, set it to NULL & return the err.
518     * Otherwise, set the length & return.
519     */
520    if (err != noErr) {
521        if ( *fullPath != NULL ) {
522            DisposeHandle(*fullPath);
523        }
524        *fullPath = NULL;
525        *length = 0;
526    }
527
528    return err;
529}
530
531// ------------------------------------------------------------------------
532
533
534//  das 271100 modified and adapted from MacTcl for Tcl on OSX
535
536
537/*
538 * tclMacResource.c --
539 *
540 *	This file contains several commands that manipulate or use
541 *	Macintosh resources.  Included are extensions to the "source"
542 *	command, the mac specific "beep" and "resource" commands, and
543 *	administration for open resource file references.
544 *
545 * Copyright (c) 1996-1997 Sun Microsystems, Inc.
546 *
547 * See the file "license.terms" for information on usage and redistribution
548 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
549 *
550 * RCS: @(#) $Id: osxMacTcl.c,v 1.1.1.1 2003/04/04 16:24:35 matben Exp $
551 */
552
553/*
554 * Pass this in the mode parameter of SetSoundVolume to determine
555 * which volume to set.
556 */
557
558enum WhichVolume {
559    SYS_BEEP_VOLUME,    /* This sets the volume for SysBeep calls */
560    DEFAULT_SND_VOLUME, /* This one for SndPlay calls */
561    RESET_VOLUME        /* And this undoes the last call to SetSoundVolume */
562};
563
564/*
565 * Prototypes for procedures defined later in this file:
566 */
567
568
569static void 		SetSoundVolume(int volume, enum WhichVolume mode);
570
571/*
572 *----------------------------------------------------------------------
573 *
574 * Tcl_BeepObjCmd --
575 *
576 *	This procedure makes the beep sound.
577 *
578 * Results:
579 *	A standard Tcl result.
580 *
581 * Side effects:
582 *	Makes a beep.
583 *
584 *----------------------------------------------------------------------
585 */
586
587int
588Tcl_BeepObjCmd(
589    ClientData dummy,			/* Not used. */
590    Tcl_Interp *interp,			/* Current interpreter. */
591    int objc,				/* Number of arguments. */
592    Tcl_Obj *CONST84 objv[])		/* Argument values. */
593{
594    Tcl_Obj *resultPtr, *objPtr;
595    Handle sound;
596    Str255 sndName;
597    int volume = -1, length;
598    char * sndArg = NULL;
599
600    resultPtr = Tcl_GetObjResult(interp);
601    if (objc == 1) {
602	SysBeep(1);
603	return TCL_OK;
604    } else if (objc == 2) {
605	if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-list")) {
606	    int count, i;
607	    short id;
608	    Str255 theName;
609	    ResType rezType;
610
611	    count = CountResources('snd ');
612	    for (i = 1; i <= count; i++) {
613		sound = GetIndResource('snd ', i);
614		if (sound != NULL) {
615		    GetResInfo(sound, &id, &rezType, theName);
616		    if (theName[0] == 0) {
617			continue;
618		    }
619		    objPtr = Tcl_NewStringObj((char *) theName + 1,
620			    theName[0]);
621		    Tcl_ListObjAppendElement(interp, resultPtr, objPtr);
622		}
623	    }
624	    return TCL_OK;
625	} else {
626	    sndArg = Tcl_GetStringFromObj(objv[1], &length);
627	}
628    } else if (objc == 3) {
629	if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-volume")) {
630	    Tcl_GetIntFromObj(interp, objv[2], &volume);
631	} else {
632	    goto beepUsage;
633	}
634    } else if (objc == 4) {
635	if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-volume")) {
636	    Tcl_GetIntFromObj(interp, objv[2], &volume);
637	    sndArg = Tcl_GetStringFromObj(objv[3], &length);
638	} else {
639	    goto beepUsage;
640	}
641    } else {
642	goto beepUsage;
643    }
644
645    /*
646     * Play the sound
647     */
648    if (sndArg == NULL) {
649	/*
650         * Set Volume for SysBeep
651         */
652
653	if (volume >= 0) {
654	    SetSoundVolume(volume, SYS_BEEP_VOLUME);
655	}
656	SysBeep(1);
657
658	/*
659         * Reset Volume
660         */
661
662	if (volume >= 0) {
663	    SetSoundVolume(0, RESET_VOLUME);
664	}
665    } else {
666	strcpy((char *) sndName + 1, sndArg);
667	sndName[0] = length;
668	sound = GetNamedResource('snd ', sndName);
669	if (sound != NULL) {
670	    /*
671             * Set Volume for Default Output device
672             */
673
674	    if (volume >= 0) {
675		SetSoundVolume(volume, DEFAULT_SND_VOLUME);
676	    }
677
678	    SndPlay(NULL, (SndListHandle) sound, false);
679
680	    /*
681             * Reset Volume
682             */
683
684	    if (volume >= 0) {
685		SetSoundVolume(0, RESET_VOLUME);
686	    }
687	} else {
688	    Tcl_AppendStringsToObj(resultPtr, " \"", sndArg,
689		    "\" is not a valid sound.  (Try ",
690		    Tcl_GetString(objv[0]), " -list)", NULL);
691	    return TCL_ERROR;
692	}
693    }
694
695    return TCL_OK;
696
697    beepUsage:
698    Tcl_WrongNumArgs(interp, 1, objv, "[-volume num] [-list | sndName]?");
699    return TCL_ERROR;
700}
701
702/*
703 *-----------------------------------------------------------------------------
704 *
705 * SetSoundVolume --
706 *
707 *	Set the volume for either the SysBeep or the SndPlay call depending
708 *	on the value of mode (SYS_BEEP_VOLUME or DEFAULT_SND_VOLUME
709 *      respectively.
710 *
711 *      It also stores the last channel set, and the old value of its
712 *	VOLUME.  If you call SetSoundVolume with a mode of RESET_VOLUME,
713 *	it will undo the last setting.  The volume parameter is
714 *      ignored in this case.
715 *
716 * Side Effects:
717 *	Sets the System Volume
718 *
719 * Results:
720 *      None
721 *
722 *-----------------------------------------------------------------------------
723 */
724
725void
726SetSoundVolume(
727    int volume,              /* This is the new volume */
728    enum WhichVolume mode)   /* This flag says which volume to
729			      * set: SysBeep, SndPlay, or instructs us
730			      * to reset the volume */
731{
732    static int hasSM3 = 1;
733    static enum WhichVolume oldMode;
734    static long oldVolume = -1;
735
736
737    /*
738     * If we don't have Sound Manager 3.0, we can't set the sound volume.
739     * We will just ignore the request rather than raising an error.
740     */
741
742    if (!hasSM3) {
743    	return;
744    }
745
746    switch (mode) {
747    	case SYS_BEEP_VOLUME:
748	    GetSysBeepVolume(&oldVolume);
749	    SetSysBeepVolume(volume);
750	    oldMode = SYS_BEEP_VOLUME;
751	    break;
752	case DEFAULT_SND_VOLUME:
753	    GetDefaultOutputVolume(&oldVolume);
754	    SetDefaultOutputVolume(volume);
755	    oldMode = DEFAULT_SND_VOLUME;
756	    break;
757	case RESET_VOLUME:
758	    /*
759	     * If oldVolume is -1 someone has made a programming error
760	     * and called reset before setting the volume.  This is benign
761	     * however, so we will just exit.
762	     */
763
764	    if (oldVolume != -1) {
765	        if (oldMode == SYS_BEEP_VOLUME) {
766	    	    SetSysBeepVolume(oldVolume);
767	        } else if (oldMode == DEFAULT_SND_VOLUME) {
768		    SetDefaultOutputVolume(oldVolume);
769	        }
770	    }
771	    oldVolume = -1;
772    }
773}
774
775
776//#endif
777