function priorityLevel=MaxPriority(varargin);
% priorityLevel=MaxPriority([windowPtrOrScreenNumber],['WaitBlanking'],['PeekBlanking'],...
% 							['BlankingInterrupt'],['SetClut'],['ClutMovie'],...
% 							['SND'],['sound'],['speak'],...
% 							['GetSecs'],['WaitSecs'],['cputime'],...
% 							['KbCheck'],['KbWait'],['CharAvail'],['GetChar'],...
% 							['EventAvail'],['GetClicks'],['GetMouse'],['GetTicks'])
%
% MaxPriority.m receives a list of one or more function names, in any
% order, and returns the maximum priorityLevel that will allow all the
% named functions to work normally on this computer. Use MaxPriority
% before calling RUSH, to select the highest priorityLevel that's
% compatible with all the functions that you're rushing.
% 
% The name matching ignores case.
% 
% If you include 'WaitBlanking', 'PeekBlanking', 'BlankingInterrupt',
% 'SetClut', or 'ClutMovie' they must be preceded by a
% windowPtrOrScreenNumber. "BlankingInterrupt" is not a function name, its
% priority is MaxPriorityForBlankingInterrupt.
% 
% PeekBlanking is complicated. If you need the GetSecs feature of
% PeekBlanking, please include both 'PeekBlanking' and 'GetSecs' in the
% call to MaxPriority. MaxPriority tries to determine whether your call to
% PeekBlanking will need the blanking interrupt. If you do need the
% interrupt then it assigns MaxPriorityForBlankingInterrupt, otherwise it
% assigns priority 7. MaxPriority decides that you need the blanking interrupt if
% SetClutPunchesBlankingClock is false or you don't include either 'SetClut' or
% 'WaitBlanking'. If you know that you need the blanking interrupt, you can
% say so by including 'BlankingInterrupt'.
% 
% MaxPriority uses RUSH's extended concept of priorityLevel. The
% standard integer priorityLevels 0 ... 7 simply set the processor
% priority. The additional new fractional priorityLevel 0.5 sets the
% processor priority to 0, but runs the code as a "deferred" task. The
% effect of this is to block all other deferred tasks, making it
% somewhat like running at raised processor priority.
% 
% The max priorityLevel associated with each function is based on
% information from Apple about the priorityLevel of the associated
% hardware interrupts (see PRIORITY), confirmed by empirical tests with
% RUSH to see how high the priority can be raised without impairing function.
% 
% WARNING: All functions that use the File Manager, e.g. FOPEN, will hang
% forever if deferred tasks are blocked, so they have max priority of 0,
% and RUSH will provide little or no benefit. For functions that can be used with
% or without the File Manager, MaxPriority assumes you're NOT using the
% File Manager. E.g. MaxPriority('fprintf') returns 7, assuming that
% you're writing to the console, but if you write to disk (which uses the
% File Manager) the max priority would be 0. Be careful; don't try to RUSH
% the File Manager; it'll hang.
% 
% MaxPriority checks to see if Virtual Memory or RAM Doubler is in
% use. They may access the disk any time you access memory (eg a
% Matlab array). For Virtual Memory, we play safe by setting MaxPriority
% to 0. For RAM Doubler, the priority is allowed its normal range. In
% either case, you're warned only once. We're taking a small
% chance here, relying on the fact that under typical conditions of
% use, RAM Doubler does not access the disk. This MaxPriority of zero
% under VM defeats most of the benefit of Rush. So we suggest that
% you buy more memory and turn off VM, to regain the benefits of Rush.
% Memory is so cheap now ($0.25/megabyte) that this is a very
% reasonable path for most users. E.g.
% web http://dealram.com/ ;
% 
% Nearly all functions (that don't use the File Manager) not included in
% the function list above are unaffected by processor priority and thus
% have a "max priority" of 7. Please let us know if something's missing.
% web mailto:psychtoolbox@yahoogroups.com ;
%
% See RUSH, Priority, MovieDemo, ScreenTest, SCREEN Preference MaxPriorityForBlankingInterrupt,
% SCREEN Preference SetClutPunchesBlankingClock.

% 2/1/98 dgp wrote it.
% 2/8/98 dgp Added priorityLevel 0.5.
% 2/28/98 dgp Mention GetSecs feature of PeekBlanking.
% 2/28/98 dgp Reduced priority for WaitBlanking and PeekBlanking from 1 to 0.5
% 3/13/98 dgp Use the new SCREEN preference MaxPriorityForBlankingInterrupt.
% 3/24/98 dgp Use SCREEN preference WaitBlankingAlwaysCallsSetClut.
% 3/24/98 dgp Add 'SetClut'. Enhance 'SND' case to check for 6100.
% 7/25/98 dgp Updated for new SCREEN Preference names.
% 8/1/98 dgp Make smarter analysis of PeekBlanking.
% 3/18/99 dgp Check for VM and RAM Doubler.
% 3/23/99 dgp Cosmetic.
% 2/4/00 dgp Updated GetSecs test to directly test for UpTime trap instead of testing for PCI.
% 3/13/01 dgp Updated to accept struct from SCREEN('Computer'), as suggested by Harriet Allen.
% 2/12/02 dgp Fixed logic for 'WaitBlanking' to allow high priority if SetClutDriverWaitsForBlanking. 
%             Eliminated unnecessary variables "analyzePeekBlanking" and "peekBlankingNeedsInterrupt".
% 4/3/02  awi Added section for Windows.  Priorities are based on the comment at the bottom of the 
%             priority help file.
% 4/9/02 dgp Give VM warning only once.
% 6/20/02  awi Decrease Win priorities for GetMouse and GetClicks from to 2 to 1. 
% 6/21/02 dgp Deleted obsolete ClutMovie option. Use new MaximumSetClutPriority pref.
% 6/22/02 dgp On second thought, DON'T use the new MaximumSetClutPriority pref. That's already
%             taken care of by the C code surrounding the SetClut call, so we don't need
% 			  to worry about it here. The rest of the MATLAB loop can usefully run at
% 			  higher priority.
% 7/29/02 awi fixed comment of 6/20/02.  

%_______________________________________________________________________________________________________________________________
%The Mac Part 

if strcmp(computer, 'MAC2')
	if nargin<1
		error(['Usage: priorityLevel=MaxPriority([windowPtrOrScreenNumber],[''WaitBlanking''],[''PeekBlanking''],...' ...
		char(13) '                        [''BlankingInterrupt''],[''SetClut''],...'...
		char(13) '                        [''SND''],[''sound''],[''speak''],...'...
		char(13) '                        [''GetSecs''],[''WaitSecs''],[''cputime''],...'...
		char(13) '                        [''KbCheck''],[''KbWait''],[''CharAvail''],[''GetChar''],...'...
		char(13) '                        [''EventAvail''],[''GetClicks''],[''GetMouse''],[''GetTicks''])']);
	end
	w=nan;
	priorityLevel=7;
	for i=1:nargin
		match=0;
		if ~ischar(varargin{i}) & ~isnumeric(varargin{i})
			error([ 'argument ' num2str(i) ' is of wrong type']);
		end
		if ischar(varargin{i})
			name=lower(varargin{i});
			if strcmp(name,lower('SND'))
				comp=SCREEN('Computer');
				if strncmp(comp.model,'PowerMac 6100',length('PowerMac 6100'))
					p=3; % on PowerMac 6100 with Mac OS 8.1
				else
					p=1;
				end
				priorityLevel=min(priorityLevel,p);
				match=1;
			end
			if strcmp(name,lower('sound'))
				priorityLevel=min(priorityLevel,0.5);
				match=1;
			end
			if strcmp(name,lower('speak'))
				priorityLevel=min(priorityLevel,0);
				match=1;
			end
			if strcmp(name,lower('CopyWindow'))
				priorityLevel=min(priorityLevel,7);
				match=1;
			end
			if strcmp(name,lower('WaitBlanking')) | strcmp(name,lower('WaitVBL'))
				if ~isfinite(w)
					error('''WaitBlanking'' must be preceded by windowPtrOrScreenNumber');
				end
				if SCREEN(w,'Preference','SetClutDriverWaitsForBlanking')
					% The priority limiting is done by the C code surrounding the SetClut call, so we don't have to worry about it.
					% The rest of our MATLAB loop can usefully run at higher priority.
% 					priorityLevel=min(priorityLevel,SCREEN(w,'Preference','MaximumSetClutPriority'));
				else
					priorityLevel=min(priorityLevel,SCREEN(w,'Preference','MaxPriorityForBlankingInterrupt'));
				end
				match=1;
			end
			if strcmp(name,lower('PeekBlanking')) | strcmp(name,lower('PeekVBL'))
				if ~isfinite(w)
					error('''PeekBlanking'' must be preceded by windowPtrOrScreenNumber');
				end
				if SCREEN(w,'Preference','SetClutDriverWaitsForBlanking') & SCREEN(w,'Preference','SetClutPunchesBlankingClock')
					% The priority limiting is done by the C code surrounding the SetClut call, so we don't have to worry about it.
					% The rest of our MATLAB loop can usefully run at higher priority.
%					priorityLevel=min(priorityLevel,SCREEN(w,'Preference','MaximumSetClutPriority'));
				else
					priorityLevel=min(priorityLevel,SCREEN(w,'Preference','MaxPriorityForBlankingInterrupt'));
				end
				match=1;
			end
			if strcmp(name,lower('BlankingInterrupt'))
				if ~isfinite(w)
					error('''BlankingInterrupt'' must be preceded by windowPtrOrScreenNumber');
				end
				priorityLevel=min(priorityLevel,SCREEN(w,'Preference','MaxPriorityForBlankingInterrupt'));
				match=1;
			end
			if strcmp(name,lower('SetClut'))
				if ~isfinite(w)
					error('''SetClut'' must be preceded by windowPtrOrScreenNumber');
				end
				if SCREEN(w,'Preference','SetClutDriverWaitsForBlanking')
					% The priority limiting is done by the C code surrounding the SetClut call, so we don't have to worry about it.
					% The rest of our MATLAB loop can usefully run at higher priority.
%					priorityLevel=min(priorityLevel,SCREEN(w,'Preference','MaximumSetClutPriority'));
				end
				if SCREEN(w,'Preference','SetClutCallsWaitBlanking')
					priorityLevel=min(priorityLevel,SCREEN(w,'Preference','MaxPriorityForBlankingInterrupt'));
				end
				match=1;
			end
			if strcmp(name,lower('GetSecs')) | strcmp(name,lower('WaitSecs'))
				available=SCREEN('Preference','Available');
				if available.UpTime
					priorityLevel=min(priorityLevel,7);
				else
					priorityLevel=min(priorityLevel,0.5);
				end
				match=1;
			end
			if strcmp(name,'cputime')
				priorityLevel=min(priorityLevel,0.5);
				match=1;
			end
			if strcmp(name,lower('GetChar'))
				priorityLevel=min(priorityLevel,0.5);
				match=1;
			end
			if strcmp(name,lower('CharAvail'))
				priorityLevel=min(priorityLevel,0.5);
				match=1;
			end
			if strcmp(name,lower('EventAvail'))
				priorityLevel=min(priorityLevel,0.5);
				match=1;
			end
			if strcmp(name,lower('GetClicks'))
				priorityLevel=min(priorityLevel,0.5);
				match=1;
			end
			if strcmp(name,lower('GetTicks'))
				priorityLevel=min(priorityLevel,0.5);
				match=1;
			end
			if strcmp(name,lower('KbCheck'))
				priorityLevel=min(priorityLevel,0.5);
				match=1;
			end
			if strcmp(name,lower('KbWait'))
				priorityLevel=min(priorityLevel,0.5);
				match=1;
			end
			if strcmp(name,lower('GetMouse'))
				priorityLevel=min(priorityLevel,0.5);
				match=1;
			end
			if strcmp(name,lower('fopen'))
				priorityLevel=min(priorityLevel,0);
				match=1;
			end
			if strcmp(name,lower('fclose'))
				priorityLevel=min(priorityLevel,0);
				match=1;
			end
			if strcmp(name,lower('fprintf'))
				priorityLevel=min(priorityLevel,7);	% assume use of console (stdout), not disk
				match=1;
			end
		end
		if isnumeric(varargin{i})
			w=varargin{i};
			match=1;
		end
		if ~match
			error(['Unknown function ''' varargin{i} '''']);
		end
	end
	% If Virtual Memory is in use then any memory access, eg to a Matlab array, may require a disk access.
	% A disk access requires that interrupts be enabled (ie. priority==0). Any attempt to access the disk 
	% will hang until priority comes down to zero. So, if necessary, we reduce priority to zero and warn the user.
	% Note that a similar test in DescribeComputer considers VM to be on if any bit is on, not just bit 32.
	bits=gestalt('vm  ');
	vm=bits(32);
	if vm
		pageToDisk=any(gestalt('vmbs')); % Is there an invisible file for VM?
	else
		pageToDisk=0;
	end
	if pageToDisk & priorityLevel>0.0
		global VMWarning
		priorityLevel=min(priorityLevel,0.0);
		s=warning;
		if strcmp(s,'backtrace')
			warning on;
		end
		if isempty(VMWarning)
			warning('MaxPriority is 0 because you''re using Virtual Memory, which might need to access the disk.');
			VMWarning=1;
		end
		warning(s);
	end
	if vm & ~pageToDisk & priorityLevel>0.0
		global RAMDoublerWarning
		if isempty(RAMDoublerWarning)
			warning('** RAM Doubler will hang forever if it tries to access the disk while you RUSH at raised priority. **');
			RAMDoublerWarning=1;
		end
	end
%_______________________________________________________________________________________________________________________________
%The Windows Part

elseif strcmp(computer, 'PCWIN')
    
	for i=1:nargin
		match=0;
		if ~ischar(varargin{i}) & ~isnumeric(varargin{i})
			error([ 'argument ' num2str(i) ' is of wrong type']);
		end
		if ischar(varargin{i})
			name=lower(varargin{i});
            priorityLevel = 2; 
            %****cases which change the priority****
			if strcmp(name,lower('CharAvail'))
				priorityLevel=min(priorityLevel,1);
				match=1;
			end
            if strcmp(name,lower('GetChar'))
				priorityLevel=min(priorityLevel,1);
				match=1;
			end
			if strcmp(name,lower('GetMouse'))
				priorityLevel=min(priorityLevel,1);
				match=1;
			end
			if strcmp(name,lower('GetClicks'))
				priorityLevel=min(priorityLevel,1);
				match=1;
			end
            %****cases which are just here to check for valid arguments****
            if strcmp(name,lower('SND'))
				match=1;
			end
			if strcmp(name,lower('sound'))
				match=1;
			end
			if strcmp(name,lower('speak'))
				match=1;
			end
			if strcmp(name,lower('CopyWindow'))
				match=1;
			end
			if strcmp(name,lower('WaitBlanking')) | strcmp(name,lower('WaitVBL'))
				match=1;
			end
			if strcmp(name,lower('PeekBlanking')) | strcmp(name,lower('PeekVBL'))
				match=1;
			end
			if strcmp(name,lower('BlankingInterrupt'))
				match=1;
			end
			if strcmp(name,lower('SetClut'))
				match=1;
			end
			if strcmp(name,lower('GetSecs')) | strcmp(name,lower('WaitSecs'))
				match=1;
			end
			if strcmp(name,'cputime')
				match=1;
			end
			if strcmp(name,lower('EventAvail'))
				match=1;
			end
			if strcmp(name,lower('GetTicks'))
				match=1;
			end
			if strcmp(name,lower('KbCheck'))
				match=1;
			end
			if strcmp(name,lower('KbWait'))
				match=1;
			end
			if strcmp(name,lower('fopen'))
				match=1;
			end
			if strcmp(name,lower('fclose'))
				match=1;
			end
			if strcmp(name,lower('fprintf'))
				match=1;
			end
        end
		if isnumeric(varargin{i})
			w=varargin{i};
			match=1;
		end
		if ~match
			error(['Unknown function ''' varargin{i} '''']);
		end
	end

%_______________________________________________________________________________________________________________________________
% neither Mac nor Win.  

else
    error('OS not supported by Psychtoolbox MaxPriority.m');
end

    
