示例#1
0
Boolean MCScreenDC::wait(real8 duration, Boolean dispatch, Boolean anyevent)
{
	MCwaitdepth++;
	real8 curtime = MCS_time();
	if (duration < 0.0)
		duration = 0.0;
	real8 exittime = curtime + duration;
	Boolean abort = False;
	Boolean reset = False;
	modalclosed = False;
	Boolean done = False;
	Boolean donepending = False;
	do
	{
		real8 eventtime = exittime;
		donepending = handlepending(curtime, eventtime, dispatch);
		siguser();
		real8 waittime = donepending ? 0.0 : eventtime - curtime;
		
		MCModeQueueEvents();
		if (MCquit)
		{
			abort = True;
			break;
		}

		if (MCnsockets > 0)
			waittime = waittime < 0.025 ? waittime: 0.025;

		//calls handle() in macdcmac.cpp
		if (modalclosed ||
			(dispatch && MCEventQueueDispatch() ||
			handle(waittime, dispatch, anyevent, abort, reset) ||
			donepending) && anyevent ||
			abort)
			break;
		if (MCquit)
		{
			abort = True;
			break;
		}

		// MW-2012-09-19: [[ Bug 10218 ]] Make sure we update the screen in case
		//   any engine event handling methods need us to.
		MCRedrawUpdateScreen();

		curtime = MCS_time();
	}
	while (curtime < exittime);
	MCwaitdepth--;
	
	// MW-2012-09-19: [[ Bug 10218 ]] Make sure we update the screen in case
	//   any engine event handling methods need us to.
	MCRedrawUpdateScreen();

	return abort;
}
示例#2
0
bool MCSessionExpireCookie()
{
	MCAutoStringRef t_session_name;
	/* UNCHECKED */ MCS_get_session_name(&t_session_name);
	
	return MCServerSetCookie(*t_session_name, MCSTR("EXPIRE"), MCS_time() - 60 * 60 * 24, nil, nil, false, true);
}
示例#3
0
// MW-2008-06-12: [[ Bug 6396 ]] Make sure r_abort is set to False if
//   the first two checks against 'button' fail. Also update the code
//   to use GetCurrentButtonState
Boolean MCScreenDC::getmouse(uint2 button, Boolean& r_abort)
{
	// Make the check interval what it always has been - 9!
	static real8 lasttime;
	real8 newtime = MCS_time();
	real8 sr = (real8)9.0 / 1000.0;
	if ((newtime - lasttime) < sr)
	{
		r_abort = MCscreen->wait(sr, False, False);
		if (r_abort)
			return False;
	}
	
	r_abort = False;
	lasttime = newtime;
	
	UInt32 t_state;
	t_state = GetCurrentButtonState();
	
	if (button == 0)
		return t_state != 0;
	
	if (button == 1)
		return (t_state & (1 << 0)) != 0;
		
	if (button == 2)
		return (t_state & (1 << 2)) != 0;
		
	if (button == 3)
		return (t_state & (1 << 1)) != 0 || ((t_state & (1 << 0)) != 0 && (querymods() & MS_MOD2) != 0);
		
	return False;
}
示例#4
0
Boolean IO_findsocket(const char *name, uint2 &i)
{
	IO_cleansockets(MCS_time());
	for (i = 0 ; i < MCnsockets ; i++)
		if (strequal(MCsockets[i]->name, name))
			return True;
	return False;
}
示例#5
0
bool MCSystemPickDate(MCDateTime *p_current, MCDateTime *p_min, MCDateTime *p_max, bool p_use_cancel, bool p_use_done, MCDateTime *r_result, bool &r_canceled, MCRectangle p_button_rect)
{
    if (s_in_popup_dialog)
        return false;
    
    int32_t t_current, t_min, t_max;
    bool t_use_min, t_use_max;

    t_use_min = p_min != nil;
    t_use_max = p_max != nil;
    
    MCExecPoint ep(nil, nil, nil);
    
    if (p_current != nil)
    {
        MCD_convert_from_datetime(ep, CF_SECONDS, CF_UNDEFINED, *p_current);
        t_current = ep.getint4();
    }
    else
        t_current = MCS_time();
    
    if (t_use_min)
    {
        MCD_convert_from_datetime(ep, CF_SECONDS, CF_UNDEFINED, *p_min);
        t_min = ep.getint4();
    }
    if (t_use_max)
    {
        MCD_convert_from_datetime(ep, CF_SECONDS, CF_UNDEFINED, *p_max);
        t_max = ep.getint4();
    }
    
    s_in_popup_dialog = true;
    s_dialog_result = kMCDialogResultUnknown;
	// IM-2012-10-31 [[ BZ 10483 ]] - make sure we have the timezone bias for the date
	MCS_getlocaldatetime(s_selected_date);
    MCAndroidEngineRemoteCall("showDatePicker", "vbbiii", nil, t_use_min, t_use_max, t_min, t_max, t_current);
    
    while(s_in_popup_dialog)
		MCscreen -> wait(60.0, True, True);

    if (s_dialog_result == kMCDialogResultError)
        return false;
    
    r_canceled = s_dialog_result == kMCDialogResultCanceled;
    if (!r_canceled)
	{
		// IM-2012-10-31 [[ BZ 10483 ]] - convert the return value back to UTC
		MCS_datetimetouniversal(s_selected_date);
        *r_result = s_selected_date;
	}
    
    return true;
}
示例#6
0
bool MCSystemPickTime(MCDateTime *p_current, MCDateTime *p_min, MCDateTime *p_max, int32_t p_step, bool p_use_cancel, bool p_use_done, MCDateTime *r_result, bool &r_canceled, MCRectangle p_button_rect)
{
    if (s_in_popup_dialog)
        return false;
    
    int32_t t_hour, t_minute;
    
    MCExecPoint ep(nil, nil, nil);
    
    MCDateTime t_current;
    if (p_current != nil)
        t_current = *p_current;
    else
    {
        ep.setnvalue(MCS_time());
        MCD_convert_to_datetime(ep, CF_SECONDS, CF_UNDEFINED, t_current);
    }
    
    // IM-2012-05-09 - make sure we show the correct local hour + minute values
    MCS_datetimetolocal(t_current);
    t_hour = t_current.hour;
    t_minute = t_current.minute;

    s_in_popup_dialog = true;
    s_dialog_result = kMCDialogResultUnknown;
	// IM-2012-10-31 [[ BZ 10483 ]] - make sure we have the timezone bias for the date
	s_selected_date = t_current;
    MCAndroidEngineRemoteCall("showTimePicker", "vii", nil, t_hour, t_minute);
    
    while (s_in_popup_dialog)
        MCscreen->wait(60.0, True, True);
    
    if (s_dialog_result == kMCDialogResultError)
        return false;
    
    r_canceled = s_dialog_result == kMCDialogResultCanceled;
    if (!r_canceled)
	{
		// IM-2012-10-31 [[ BZ 10483 ]] - convert the return value back to UTC
		MCS_datetimetouniversal(s_selected_date);
        *r_result = s_selected_date;
	}
    
    return true;
}
示例#7
0
bool MCSessionExpireSession(MCStringRef p_id)
{
	bool t_success = true;
	
	MCSession *t_session = NULL;
	MCSessionIndexRef t_index = NULL;
	
	t_success = MCSessionOpenIndex(t_index);
	if (t_success)
	{
		if (MCSessionFindMatchingSession(t_index, p_id, t_session))
			t_session->expires = MCS_time() - 60 * 60 * 24;
	}
	if (t_index)
		t_success &= MCSessionCloseIndex(t_index, t_success);
	
	return t_success;
}
示例#8
0
bool MCSessionOpenSession(MCSessionIndexRef p_index, MCSession *p_session)
{
	bool t_success = true;
	
	MCAutoStringRef t_path_string;
    t_success = MCStringFormat(&t_path_string, "%@/%s", p_index->save_path, p_session->filename);
	
	if (t_success)
		t_success = NULL != (p_session->filehandle = MCsystem->OpenFile(*t_path_string, kMCOpenFileModeUpdate, false));
	
	if (t_success)
		t_success = MCSystemLockFile(p_session->filehandle, false, true);
	
	if (t_success && p_session->filehandle->GetFileSize() > 0 && p_session->expires > MCS_time())
		t_success = MCSessionReadSession(p_session);
	
	return t_success;
}
示例#9
0
bool MCSessionCleanup(void)
{
	bool t_success = true;
	
	MCSessionIndexRef t_index = NULL;
	t_success = MCSessionOpenIndex(t_index);
	
	real8 t_time;
	t_time = MCS_time();
	
	for (uint32_t i = 0; t_success && i < t_index->session_count; i++)
	{
		if (t_index->session[i]->expires <= t_time)
		{
			bool t_deleted = false;
			// check file not locked
			MCSystemFileHandle *t_file;
			MCAutoStringRef t_full_path_string;
            if (MCStringFormat(&t_full_path_string, "%@/%s", t_index->save_path, t_index->session[i]->filename)  && MCS_exists(*t_full_path_string, True))
			{
				t_file = MCsystem->OpenFile(*t_full_path_string, kMCOpenFileModeRead, false);
				if (t_file != NULL)
				{
					bool t_locked = false;
					t_locked = MCSystemLockFile(t_file, false, false);
					t_file->Close();
					
					if (t_locked)
						t_deleted = MCsystem->DeleteFile(*t_full_path_string);
				}
			}
			else
				t_deleted = true;

			if (t_deleted)
				MCSessionIndexRemoveSession(t_index, t_index->session[i]);
		}
	}
	
	if (t_index != NULL)
		t_success &= MCSessionCloseIndex(t_index, t_success);
	
	return t_success;
}
示例#10
0
void IO_freeobject(MCObject *o)
{
	IO_cleansockets(MCS_time());
	uint2 i = 0;
	while (i < MCnsockets)
#if 1
	{
		if (MCsockets[i]->object == o)
			MCsockets[i]->doclose();
		i++;
	}
#else
		if (MCsockets[i]->object == o)
		{
			delete MCsockets[i];
			uint2 j = i;
			while (++j < MCnsockets)
				MCsockets[j - 1] = MCsockets[j];
			MCnsockets--;
		}
		else
			i++;
#endif
}
示例#11
0
void MCInternalObjectListenerMessagePendingListeners(void)
{
	if (MCobjectpropertieschanged)
	{
		MCobjectpropertieschanged = False;
		
		MCObjectListener *t_prev_listener;
		t_prev_listener = nil;
		
		MCObjectListener *t_listener;
		t_listener = s_object_listeners;
		while(t_listener != nil)
		{
			// MW-2013-08-27: [[ Bug 11126 ]] This static is updated by the remove_* functions
			//   to ensure we don't get any dangling pointers.
			s_next_listener_to_process = t_listener -> next;
			
			if (!t_listener -> object -> Exists())
				remove_object_listener_from_list(t_listener, t_prev_listener);
			else
			{
				uint8_t t_properties_changed;
				t_properties_changed = t_listener -> object -> Get() -> propertieschanged();
				if (t_properties_changed != kMCPropertyChangedMessageTypeNone)
				{			
					MCExecPoint ep(nil, nil, nil);
					t_listener -> object -> Get() -> getprop(0, P_LONG_ID, ep, False);			
					
					MCObjectListenerTarget *t_target;
					t_target = nil;
					MCObjectListenerTarget *t_prev_target;
					t_prev_target = nil;	
					
					double t_new_time;
					t_new_time = MCS_time();
					if (t_listener -> last_update_time + MCpropertylistenerthrottletime / 1000.0 < t_new_time)
					{
						t_listener -> last_update_time = t_new_time;
						t_target = t_listener->targets;
						while (t_target != nil)
						{
							// MW-2013-08-27: [[ Bug 11126 ]] This static is updated by the remove_* functions
							//   to ensure we don't get any dangling pointers.
							s_next_listener_target_to_process = t_target -> next;
							
							if (!t_target -> target -> Exists())
								remove_object_listener_target_from_list(t_target, t_prev_target, t_listener, t_prev_listener);
							else
							{
								// MM-2012-11-06: Added resizeControl(Started/Ended) and gradientEdit(Started/Ended) messages.
								if (t_properties_changed & kMCPropertyChangedMessageTypePropertyChanged)								
									t_target -> target -> Get() -> message_with_args(MCM_property_changed, ep . getsvalue());
								if (t_properties_changed & kMCPropertyChangedMessageTypeResizeControlStarted)								
									t_target -> target -> Get() -> message_with_args(MCM_resize_control_started, ep . getsvalue());
								if (t_properties_changed & kMCPropertyChangedMessageTypeResizeControlEnded)								
									t_target -> target -> Get() -> message_with_args(MCM_resize_control_ended, ep . getsvalue());
								if (t_properties_changed & kMCPropertyChangedMessageTypeGradientEditStarted)								
									t_target -> target -> Get() -> message_with_args(MCM_gradient_edit_started, ep . getsvalue());
								if (t_properties_changed & kMCPropertyChangedMessageTypeGradientEditEnded)								
									t_target -> target -> Get() -> message_with_args(MCM_gradient_edit_ended, ep . getsvalue());
								
								t_prev_target = t_target;					
							}
							
							t_target = s_next_listener_target_to_process;
						}
					}
					else
						t_listener -> object -> Get() -> signallistenerswithmessage(t_properties_changed);
				}
				
				t_prev_listener = t_listener;
			}
			
			// MW-2013-08-27: [[ Bug 11126 ]] Use the static as the next in the chain.
			t_listener = s_next_listener_to_process;
		}
	}
}
示例#12
0
void MCSessionRefreshExpireTime(MCSession *p_session)
{
	p_session->expires = MCS_time() + MCS_get_session_lifetime();
}
示例#13
0
void MCStack::effectrect(const MCRectangle& p_area, Boolean& r_abort)
{
	// Get the list of effects.
	MCEffectList *t_effects = MCcur_effects;
	MCcur_effects = NULL;

	// If the window isn't opened or hasn't been attached (plugin) or if we have no
	// snapshot to use, this is a no-op.
	if (!opened || !haswindow() || m_snapshot == nil)
	{
		while(t_effects != NULL)
		{
			MCEffectList *t_effect;
			t_effect = t_effects;
			t_effects = t_effects -> next;
			delete t_effect;
		}
		return;
	}

	// Mark the stack as being in an effect.
	state |= CS_EFFECT;

	// Lock messages while the effect is happening.
	Boolean t_old_lockmessages;
	t_old_lockmessages = MClockmessages;
	MClockmessages = True;

	// Calculate the area of interest.
	MCRectangle t_effect_area;
	t_effect_area = curcard -> getrect();
	t_effect_area . y = getscroll();
	t_effect_area . height -= t_effect_area . y;
	t_effect_area = MCU_intersect_rect(t_effect_area, p_area);
	
	// IM-2013-08-21: [[ ResIndependence ]] Scale effect area to device coords
	// Align snapshot rect to device pixels
	// IM-2013-09-30: [[ FullscreenMode ]] Use stack transform to get device coords
	MCGAffineTransform t_transform;
	t_transform = getdevicetransform();
	
    // MW-2013-10-29: [[ Bug 11330 ]] Make sure the effect area is cropped to the visible
    //   area.
    t_effect_area = MCRectangleGetTransformedBounds(t_effect_area, getviewtransform());
    t_effect_area = MCU_intersect_rect(t_effect_area, MCU_make_rect(0, 0, view_getrect() . width, view_getrect() . height));
	
	// IM-2014-01-24: [[ HiDPI ]] scale effect region to backing surface coords
	MCGFloat t_scale;
	t_scale = view_getbackingscale();
	
    MCRectangle t_device_rect, t_user_rect;
	t_device_rect = MCRectangleGetScaledBounds(t_effect_area, t_scale);
	t_user_rect = MCRectangleGetTransformedBounds(t_device_rect, MCGAffineTransformInvert(t_transform));
	
	// IM-2013-08-29: [[ RefactorGraphics ]] get device height for CoreImage effects
	// IM-2013-09-30: [[ FullscreenMode ]] Use view rect to get device height
	uint32_t t_device_height;
	t_device_height = floor(view_getrect().height * t_scale);
	
	// Make a region of the effect area
	// IM-2013-08-29: [[ ResIndependence ]] scale effect region to device coords
	MCRegionRef t_effect_region;
	t_effect_region = nil;
	/* UNCHECKED */ MCRegionCreate(t_effect_region);
	/* UNCHECKED */ MCRegionSetRect(t_effect_region, t_effect_area);
	
#ifndef FEATURE_PLATFORM_PLAYER
#if defined(FEATURE_QUICKTIME)
	// MW-2010-07-07: Make sure QT is only loaded if we actually are doing an effect
	if (t_effects != nil)
		if (!MCdontuseQTeffects)
			if (!MCtemplateplayer -> isQTinitted())
				MCtemplateplayer -> initqt();
#endif	
#endif

	// Lock the screen to prevent any updates occuring until we want them.
	MCRedrawLockScreen();

	// By default, we have not aborted.
	r_abort = False;
	
	MCGImageRef t_initial_image;
	t_initial_image = MCGImageRetain(m_snapshot);
	
	while(t_effects != nil)
	{
		uint32_t t_duration;
		t_duration = MCU_max(1, MCeffectrate / (t_effects -> speed - VE_VERY));
		if (t_effects -> type == VE_DISSOLVE)
			t_duration *= 2;
		
		uint32_t t_delta;
		t_delta = 0;
		
		// Create surface at effect_area size.
		// Render into surface based on t_effects -> image
		MCGImageRef t_final_image = nil;
		
		// If this isn't a plain effect, then we must fetch first and last images.
		if (t_effects -> type != VE_PLAIN)
		{
			// Render the final image.
			MCGContextRef t_context = nil;
			
			// IM-2014-05-20: [[ GraphicsPerformance ]] Create opaque context for snapshot
			/* UNCHECKED */ MCGContextCreate(t_device_rect.width, t_device_rect.height, false, t_context);
			
			MCGContextTranslateCTM(t_context, -t_device_rect.x, -t_device_rect.y);
			
			// IM-2013-10-03: [[ FullscreenMode ]] Apply device transform to context
			MCGContextConcatCTM(t_context, t_transform);
			
			// Configure the context.
			MCGContextClipToRect(t_context, MCRectangleToMCGRectangle(t_user_rect));
			
			// Render an appropriate image
			switch(t_effects -> image)
			{
				case VE_INVERSE:
					{
						MCContext *t_old_context = nil;
						/* UNCHECKED */ t_old_context = new MCGraphicsContext(t_context);
						curcard->draw(t_old_context, t_user_rect, false);
						delete t_old_context;
						
						MCGContextSetFillRGBAColor(t_context, 1.0, 1.0, 1.0, 1.0);
						MCGContextSetBlendMode(t_context, kMCGBlendModeDifference);
						MCGContextAddRectangle(t_context, MCRectangleToMCGRectangle(t_user_rect));
						MCGContextFill(t_context);
					}
					break;
					
				case VE_BLACK:
					MCGContextSetFillRGBAColor(t_context, 0.0, 0.0, 0.0, 1.0);
					MCGContextAddRectangle(t_context, MCRectangleToMCGRectangle(t_user_rect));
					MCGContextFill(t_context);
					break;
					
				case VE_WHITE:
					MCGContextSetFillRGBAColor(t_context, 1.0, 1.0, 1.0, 1.0);
					MCGContextAddRectangle(t_context, MCRectangleToMCGRectangle(t_user_rect));
					MCGContextFill(t_context);
					break;
					
				case VE_GRAY:
					MCGContextSetFillRGBAColor(t_context, 0.5, 0.5, 0.5, 1.0);
					MCGContextAddRectangle(t_context, MCRectangleToMCGRectangle(t_user_rect));
					MCGContextFill(t_context);
					break;
					
				default:
				{
					MCContext *t_old_context = nil;
					/* UNCHECKED */ t_old_context = new MCGraphicsContext(t_context);
					curcard->draw(t_old_context, t_user_rect, false);
					delete t_old_context;
				}
			}
			
			/* UNCHECKED */ MCGContextCopyImage(t_context, t_final_image);
			MCGContextRelease(t_context);
		}
		
		MCStackEffectContext t_context;
		t_context.delta = t_delta;
		t_context.duration = t_duration;
		t_context.effect = t_effects;
		t_context.effect_area = t_device_rect;
		t_context.initial_image = t_initial_image;
		t_context.final_image = t_final_image;
		
		// MW-2011-10-20: [[ Bug 9824 ]] Make sure dst point is correct.
		// Initialize the destination with the start image.
		view_platform_updatewindowwithcallback(t_effect_region, MCStackRenderInitial, &t_context);
		
		// If there is a sound, then start playing it.
		if (t_effects -> sound != NULL)
		{
			MCAudioClip *acptr;
            MCNewAutoNameRef t_sound;
            /* UNCHECKED */ MCNameCreate(t_effects->sound, &t_sound);
			if ((acptr = (MCAudioClip *)getobjname(CT_AUDIO_CLIP, *t_sound)) == NULL)
			{
				IO_handle stream;
				if ((stream = MCS_open(t_effects->sound, kMCOpenFileModeRead, True, False, 0)) != NULL)
				{
					acptr = new MCAudioClip;
					acptr->setdisposable();
					if (!acptr->import(t_effects->sound, stream))
					{
						delete acptr;
						acptr = NULL;
					}
					MCS_close(stream);
				}
			}
			
			if (acptr != NULL)
			{
				MCU_play_stop();
				MCacptr = acptr;
				MCU_play();
#ifndef FEATURE_PLATFORM_AUDIO
				if (MCacptr != NULL)
					MCscreen->addtimer(MCacptr, MCM_internal, PLAY_RATE);
#endif
			}
			
			if (MCscreen->wait((real8)MCsyncrate / 1000.0, False, True))
			{
				r_abort = True;
				break;
			}
		}
		
		// Initialize CoreImage of QTEffects if needed.
		if (t_effects -> type != VE_PLAIN)
		{
            MCAutoPointer<char> t_name;
            /* UNCHECKED */ MCStringConvertToCString(t_effects -> name, &t_name);
#ifdef _MAC_DESKTOP
			// IM-2013-08-29: [[ ResIndependence ]] use scaled effect rect for CI effects
			if (t_effects -> type == VE_UNDEFINED && MCCoreImageEffectBegin(*t_name, t_initial_image, t_final_image, t_device_rect, t_device_height, t_effects -> arguments))
				t_effects -> type = VE_CIEFFECT;
			else
#endif
#ifdef FEATURE_QUICKTIME_EFFECTS
				// IM-2013-08-29: [[ ResIndependence ]] use scaled effect rect for QT effects
				if (t_effects -> type == VE_UNDEFINED && MCQTEffectBegin(t_effects -> type, *t_name, t_effects -> direction, t_initial_image, t_final_image, t_device_rect))
					t_effects -> type = VE_QTEFFECT;
#else
				;
#endif
		}
		
		// Run effect
		// Now perform the effect loop, but only if there is something to do.
		if (t_effects -> type != VE_PLAIN || old_blendlevel != blendlevel)
		{
			// Calculate timing parameters.
			double t_start_time;
			t_start_time = 0.0;
			
			for(;;)
			{
				t_context.delta = t_delta;
				
				Boolean t_drawn = False;
				view_platform_updatewindowwithcallback(t_effect_region, MCStackRenderEffect, &t_context);
				
				// Now redraw the window with the new image.
//				if (t_drawn)
				{
					MCscreen -> sync(getw());
				}
				
				// Update the window's blendlevel (if needed)
				if (old_blendlevel != blendlevel)
				{
					float t_fraction = float(t_delta) / t_duration;
					setopacity(uint1((old_blendlevel * 255 + (float(blendlevel) - old_blendlevel) * 255 * t_fraction) / 100));
				}
				
				// If the start time is zero, then start counting from here.
				if (t_start_time == 0.0)
					t_start_time = MCS_time();
				
				// If we've reached the end of the transition, we are done.
				if (t_delta == t_duration)
				{
#ifdef _ANDROID_MOBILE
					// MW-2011-12-12: [[ Bug 9907 ]] Make sure we let the screen sync at this point
					MCscreen -> wait(0.01, False, False);
#endif
					break;
				}
				
				// Get the time now.
				double t_now;
				t_now = MCS_time();
				
				// Compute the new delta value.
				uint32_t t_new_delta;
				t_new_delta = (uint32_t)ceil((t_now - t_start_time) * 1000.0);
				
				// If the new value is same as the old, then advance one step.
				if (t_new_delta == t_delta)
					t_delta = t_new_delta + 1;
				else
					t_delta = t_new_delta;
				
				// If the new delta is beyond the end point, set it to the end.
				if (t_delta > t_duration)
					t_delta = t_duration;
				
				// Wait until the next boundary, making sure we break for no reason
				// other than abort.
				if (MCscreen -> wait((t_start_time + (t_delta / 1000.0)) - t_now, False, False))
					r_abort = True;
				
				// If we aborted, we render the final step and are thus done.
				if (r_abort)
					t_delta = t_duration;
			}
		}		
#ifdef _MAC_DESKTOP
		if (t_effects -> type == VE_CIEFFECT)
			MCCoreImageEffectEnd();
		else
#endif
#ifdef FEATURE_QUICKTIME_EFFECTS
			if (t_effects -> type == VE_QTEFFECT)
				MCQTEffectEnd();
#endif
		
		// Free initial surface.
		MCGImageRelease(t_initial_image);
		
		// initial surface becomes final surface.
		t_initial_image = t_final_image;
		t_final_image = nil;
		
		// Move to the next effect.
		MCEffectList *t_current_effect;
		t_current_effect = t_effects;
		t_effects = t_effects -> next;
		delete t_current_effect;
	}

	// Make sure the pixmaps are freed and any dangling effects
	// are cleaned up.
	if (t_effects != NULL)
	{
		/* OVERHAUL - REVISIT: error cleanup needs revised */
		MCGImageRelease(t_initial_image);
//		MCGSurfaceRelease(t_final_image);

		while(t_effects != NULL)
		{
			MCEffectList *t_current_effect;
			t_current_effect = t_effects;
			t_effects = t_effects -> next;
			delete t_current_effect;
		}
	}

	MCRegionDestroy(t_effect_region);
	
	MCGImageRelease(m_snapshot);
	m_snapshot = nil;
	
	m_snapshot = t_initial_image;
	
	// Unlock the screen.
	MCRedrawUnlockScreen();
	
	// Unlock messages.
	MClockmessages = t_old_lockmessages;

	// Turn off effect mode.
	state &= ~CS_EFFECT;
	
	// The stack's blendlevel is now the new one.
	old_blendlevel = blendlevel;
	
	// Finally, mark the affected area of the stack for a redraw.
	dirtyrect(p_area);
}