activegs-ios/kegs/iOS/zoomEmulatorView.mm

815 lines
19 KiB
Plaintext

/*
ActiveGS, Copyright 2004-2016 Olivier Goguel, https://github.com/ogoguel/ActiveGS
Based on Kegs, Copyright 2004 Kent Dickey, https://kegs.sourceforge.net
This code is covered by the GNU GPL licence
*/
#include "../Src/defc.h"
#include "../Src/sim65816.h"
#include "../Src/adb.h"
#import "zoomEmulatorView.h"
#ifdef ACTIVEGSKARATEKA
#import "../../ActiveGS_Karateka/KaratekaAppDelegate.h"
#elif defined(ACTIVEGS)
#import "../../Common.iphone/activegsAppDelegate.h"
#endif
#undef debug_printf
//#define debug_printf(...)
#define debug_printf printf
//#define SHOW_COLOR
@implementation zoomEmulatorView
@synthesize ew = _ew;
@synthesize crt = _crt;
@synthesize useTouch = _useTouch;
@synthesize secondTouch = _secondTouch;
- (id)initWithFrame:(CGRect)frame ratio:(float)ratio
{
if (self = [super initWithFrame:frame])
{
self->dpiRatio = ratio;
self->viewSize = frame.size;
debug_printf("ratio:%f width:%f height:%f",self->dpiRatio,self->viewSize.width,self->viewSize.height);
CGRect r = CGRectMake(0.0,0.0,X_A2_WINDOW_WIDTH/self->dpiRatio ,X_A2_WINDOW_HEIGHT/self->dpiRatio );
self.contentSize = CGSizeMake(r.size.width,r.size.height);
self.ew = [[emulatorView alloc] initWithFrame:r];
[self.ew setUserInteractionEnabled:TRUE];
CGColorSpaceRef innerColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef icontext = CGBitmapContextCreate(nil, X_A2_WINDOW_WIDTH,X_A2_WINDOW_HEIGHT*2, 8, X_A2_WINDOW_WIDTH*4,innerColorSpace,kCGImageAlphaPremultipliedLast);
CGContextSetRGBFillColor(icontext,0,0,0,0.0); // transparent
CGContextFillRect(icontext,CGRectMake(0,0,X_A2_WINDOW_WIDTH,X_A2_WINDOW_HEIGHT*2));
for(int h=0;h<X_A2_WINDOW_HEIGHT;h++)
{
CGRect rr = CGRectMake(0,h*2,X_A2_WINDOW_WIDTH,1);
CGContextSetRGBFillColor(icontext,0,0,0,0.5); // transparent
CGContextFillRect(icontext,rr);
}
CGImageRef innerImageRef = CGBitmapContextCreateImage(icontext);
UIImage* img = [UIImage imageWithCGImage:innerImageRef];
self.crt = [[UIImageView alloc]initWithImage:img];
self.crt.transform = CGAffineTransformMakeScale(1,0.5);
[self.crt setFrame:CGRectMake(0,0,r.size.width,r.size.height)];
[self.crt setBounds:CGRectMake(0,0,r.size.width,r.size.height*2)];
self.crt.hidden = (r_sim65816.get_video_fx()==VIDEOFX_CRT?0:1);
[self.ew addSubview:self.crt];
CFRelease(icontext);
CFRelease(innerImageRef);
self.maximumZoomScale = 3.0;
self.minimumZoomScale = 0.25;
self.delegate = self;
self.scrollEnabled = FALSE;
self.canCancelContentTouches = NO; // interdiction d'annuler une fois que la vue a commencé a récupéré les infs
// Recherche les Gestures
for (UIGestureRecognizer *gestureRecognizer in self.gestureRecognizers) {
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
panGR = (UIPanGestureRecognizer *) gestureRecognizer;
else
if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]])
pinchGR = (UIPinchGestureRecognizer *) gestureRecognizer;
}
panGR.minimumNumberOfTouches = 2;
self.scrollEnabled = TRUE;
self.pagingEnabled = NO;
self.bounces = YES;
self.clipsToBounds = YES;
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
// self.canCancelContentTouches = NO; // interdiction d'annuler une fois que la vue a commencé a récupéré les infs
#ifdef SHOW_COLOR
[self setBackgroundColor:[UIColor redColor]];
[self.ew setBackgroundColor:[UIColor greenColor]];
#else
[self setBackgroundColor:[UIColor blackColor]];
[self.ew setBackgroundColor:[UIColor blackColor]];
#endif
[self addSubview:self.ew];
}
return self;
}
int inCran=0;
float refScale=0.5f;
float refScalePortrait;
float refScaleLandscape;
-(void)updateScreen:(CGRect)frame ratio:(float)ratio
{
// [self updateFrame:frame];
// [self setBounds:frame];
self->viewSize = frame.size;
self->dpiRatio = ratio;
CGRect r = CGRectMake(0.0,0.0,X_A2_WINDOW_WIDTH/self->dpiRatio ,X_A2_WINDOW_HEIGHT/self->dpiRatio );
self.contentSize = CGSizeMake(r.size.width,r.size.height);
[self.ew setFrame:r];
[self.crt setFrame:CGRectMake(0,0,r.size.width,r.size.height)];
[self.crt setBounds:CGRectMake(0,0,r.size.width,r.size.height*2)];
[self setRatioToOne];
}
-(void)updateFrame:(CGRect)frame kbdRect:(CGRect)kbdRect
{
// adapte la vue pour que le clavier ne cache pas l'écran
CGRect scrollSize = self.bounds ;
float ref = kbdRect.size.height;
if ( (-scrollSize.origin.y) < ref)
{
// centre par rapport à la hauteur restante
float hremaining = frame.size.height - ref;
frame.origin.y = - kbdRect.size.height- ( hremaining - scrollSize.size.height*HEIGHT_MULTIPLY ) /2 ;
// TODO : ajuster le zoom!
}
// NSLog(@"Frame %f,%f %f,%f",frame.origin.x,frame.origin.y,frame.size.width,frame.size.height);
[self setFrame:frame];
}
-(void)setRatioToOne
{
int w = (int)viewSize.width;
int h = (int)viewSize.height;
// Force Portrait
if (w > h)
{
w = (int)viewSize.height;
h = (int)viewSize.width;
}
debug_printf("apprect %d x %d\n",w,h);
#define ZOOM_CLAMP 0.25 // calcul un zoom par multiple *.25
for(int i=0;i<4;i++)
{
if (!(i&1))
{
// prend le plus petit des zooms possible
float r = w*dpiRatio / (A2_WINDOW_WIDTH*WIDTH_MULTIPLY);
r = r/ZOOM_CLAMP;
if (r<1.0)
r = 1.0;
int ir = (int)r;
float zoomw = (float)ir*ZOOM_CLAMP;
r = h*dpiRatio / (A2_WINDOW_HEIGHT*HEIGHT_MULTIPLY);
r = r/ZOOM_CLAMP;
if (r<1.0)
r = 1.0;
ir = (int)r;
float zoomh = (float)ir*ZOOM_CLAMP;
float zoom = zoomh < zoomw ? zoomh : zoomw ;
orientationVars[i].scale = zoom;
float realew =(X_A2_WINDOW_WIDTH - A2_WINDOW_WIDTH)*WIDTH_MULTIPLY*zoom;
float realw = w*dpiRatio;
float posw = realw - A2_WINDOW_WIDTH*zoom;
float x = (posw - realew)/2; // pour centrer
float realeh =(X_A2_WINDOW_HEIGHT - A2_WINDOW_HEIGHT)*HEIGHT_MULTIPLY*zoom;
float realh = h*dpiRatio;
float posh = realh - A2_WINDOW_HEIGHT*HEIGHT_MULTIPLY*zoom;
float y = (posh - realeh)/2; // pour centrer
orientationVars[i].p = CGPointMake(-x/dpiRatio/WIDTH_MULTIPLY,-y/dpiRatio/HEIGHT_MULTIPLY);
}
else
{
float r = h*dpiRatio / A2_WINDOW_WIDTH;
r = r/ZOOM_CLAMP;
if (r<1.0)
r = 1.0;
int ir = (int)r;
float zoomw = (float)ir*ZOOM_CLAMP;
r = w*dpiRatio / ( A2_WINDOW_HEIGHT * HEIGHT_MULTIPLY );
r = r/ZOOM_CLAMP;
if (r<1.0)
r = 1.0;
ir = (int)r;
float zoomh = (float)ir*ZOOM_CLAMP;
float zoom = zoomh < zoomw ? zoomh : zoomw ;
float realew =(X_A2_WINDOW_WIDTH - A2_WINDOW_WIDTH)*zoom;
float realw = h*dpiRatio;
float posw = realw - A2_WINDOW_WIDTH*zoom;
float x = (posw - realew)/2;
float realeh =(X_A2_WINDOW_HEIGHT - A2_WINDOW_HEIGHT)*HEIGHT_MULTIPLY*zoom;
float realh = w*dpiRatio;
float posh = realh - A2_WINDOW_HEIGHT*HEIGHT_MULTIPLY*zoom;
float y = (posh - realeh)/2; // pour centrer
orientationVars[i].scale = zoom;
orientationVars[i].p = CGPointMake(-x/dpiRatio,-y/dpiRatio/2);
}
debug_printf("orientation:%d = %f , p = %f,%f\n",i, orientationVars[i].scale,orientationVars[i].p.x,orientationVars[i].p.y);
}
mouseDown = 0;
allowInput = TRUE;
zoomDisabled = FALSE;
refScalePortrait = orientationVars[0].scale;
refScaleLandscape = orientationVars[1].scale;
}
- (void)switchOrientation:(int)orientation
{
curOrientation = -1;
float s = orientationVars[orientation].scale;
CGPoint p = orientationVars[orientation].p;
debug_printf("**** about to switchOrientation: (%d) scale:%f offset %f,%f\n",orientation,s,p.x,p.y);
self.zoomScale = s ;
[self setContentOffset:p];
curOrientation = orientation;
s = orientationVars[orientation].scale;
p = orientationVars[orientation].p;
debug_printf("**** done switching (%d) scale:%f offset %f,%f\n",orientation,s,p.x,p.y);
inCran = false;
if (orientation & 1)
refScale = refScaleLandscape;
else
refScale = refScalePortrait;
}
-(void)setContentOffset:(CGPoint)p /*animated:(BOOL)animated*/
{
if (p.x == 0.0f || p.y == 0.0)
{
debug_printf("ignoring %f,%f\n",p.x,p.y);
return ;
}
// appelé par [super initWithFrame:frame] alors que ew n'est pas encore initialisé
if(self.ew != nil )
{
CGSize vs = self.ew.frame.size;
CGSize scrollSize = self.bounds.size;
static float realScale=0.0f;
if (!inCran)
{
if ( fabs(self.zoomScale - refScale) < 0.05 )
{
inCran = true;
realScale = self.zoomScale;
self.zoomScale = refScale;
}
}
else
{
realScale += self.zoomScale - refScale;
if (fabs(realScale - refScale) > 0.10 )
{
// à faire avant le sef.zoomScale= sinon on tombe dans une boucle infinie!
inCran = false;
self.zoomScale = realScale;
}
else
self.zoomScale = refScale;
}
// recentre la fenetre si elle tient totalement en hauteur ou en largeur
if (scrollSize.width >= vs.width || inCran )
p.x = - (scrollSize.width - abs(vs.width)) / 2.0;
if (scrollSize.height >= vs.height || inCran )
p.y = - (scrollSize.height - abs(vs.height)) / 2.0;
if (curOrientation!=-1)
{
debug_printf("saving scale (%d) %f %f,%f\n",curOrientation,self.zoomScale,p.x,p.y);
orientationVars[curOrientation].scale = self.zoomScale ; // / kbdScale;
orientationVars[curOrientation].p = p;
}
}
debug_printf("********** offset : %f %f\n",p.x,p.y);
[super setContentOffset:p];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.ew;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef g = UIGraphicsGetCurrentContext();
#ifdef SHOW_COLOR
CGContextSetFillColorWithColor(g, [UIColor blueColor].CGColor);
#else
CGContextSetFillColorWithColor(g, [UIColor blackColor].CGColor);
#endif
CGContextFillRect(g, rect);
}
-(CGPoint)rotateTouch:(UITouch*) _touch
{
CGPoint pt;
float scale = self->dpiRatio/self.zoomScale;
if (g_adb.g_warp_pointer==WARP_TOUCHSCREEN)
{
pt = [_touch locationInView:self.ew];
pt.x *= self->dpiRatio;
pt.y *= self->dpiRatio;
pt.y = (X_A2_WINDOW_HEIGHT - pt.y);
pt.y *=2;
pt.x -= BASE_MARGIN_LEFT;
pt.y -= BASE_MARGIN_TOP*2;
return pt;
}
pt = [_touch locationInView:self];
int height = self.contentSize.height;
if (r_sim65816.is_emulator_in_640mode())
{
// current scale for 640 mode
}
else
{
// otherwise reduce the speed of the mouse
scale /= 2;
}
pt.y = (height - pt.y)*scale;
pt.x *= scale;
return pt;
}
-(BOOL)touchesShouldCancelInContentView:(UIView *)view
{
debug_printf("touchesShouldCancelInContentView\n");
return !zoomDisabled;
}
-(void)enableInput
{
allowInput = TRUE;
}
-(void)disableInput
{
allowInput = FALSE;
if (self.useTouch)
self.useTouch = nil;
if (self.secondTouch)
{
self.secondTouch = nil;
printf("*** secondtouch set in disableinput --- should never appear");
}
}
-(void)disableZoom
{
debug_printf("disableZoom\n");
// BUG!!!!
// Sometimes, offset is being reset when gestures are disabled!
// Workaround = save the former offset and restore it afterwards
CGPoint offset = self.contentOffset;
pinchGR.enabled = FALSE;
panGR.enabled = FALSE;
self.scrollEnabled = FALSE;
zoomDisabled= TRUE;
// REstoring the offset
self.contentOffset = offset;
}
-(void)enableZoom
{
debug_printf("enableZoom\n");
pinchGR.enabled = TRUE;
panGR.enabled = TRUE;
self.scrollEnabled = TRUE;
zoomDisabled = FALSE;
}
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
debug_printf("scrollViewWillBeginZooming\n");
#ifdef ACTIVEGS
// le zoom a démarré : anule les timers pour que le zoom ne soit pas interrompu
[[pManager getEmulatorView].contentView disableGestures:(MODE_ALL + MODE_EMULATOR & ~MODE_ZOOM)];
[[pManager getEmulatorView].kbdc setGestureIndicatorVisibility:MODE_ZOOM color:[UIColor grayColor]];
#endif
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
debug_printf("scrollViewDidEndZooming\n");
#ifdef ACTIVEGS
// zoom terminé => on peut réactiver les gestures immédiatement
[[pManager getEmulatorView].contentView reenableAllGestures];
#endif
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// NSLog(@" Offset = %@ ",NSStringFromCGPoint(scrollView.contentOffset));
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
// printf("scrollViewDidZoom");
// [ew directDraw:nil];
}
- (void) mouseDown
{
if (!mouseDown)
{
debug_printf("mouseDown %f %f\n",lastMousePos.x,lastMousePos.y);
mouseDown = 1;
add_event_mouse(lastMousePos.x,lastMousePos.y,1,1);
#ifdef ACTIVEGS
[[pManager getEmulatorView].kbdc setGestureIndicatorVisibility:MODE_MOUSEBUTTON color:[UIColor redColor]];
#endif
}
}
- (void) mouseUp
{
if (mouseDown)
{
add_event_mouse(lastMousePos.x,lastMousePos.y,0,1);
mouseDown = 0;
#ifdef ACTIVEGS
[[pManager getEmulatorView].kbdc setGestureIndicatorVisibility:MODE_MOUSEBUTTON color:[UIColor greenColor]];
#endif
}
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
#ifdef ACTIVEGS
if ([[pManager getEmulatorView].kbdc myTouchesBegan:touches])
{
debug_printf("pad activated");
[[pManager getEmulatorView].kbdc setGestureIndicatorVisibility:MODE_PAD color:[UIColor grayColor]];
[[pManager getEmulatorView].contentView disableGestures:(MODE_ALL+MODE_EMULATOR & ~MODE_PAD)];
return ;
}
#endif
// prend la première touche
UITouch *touch = [[touches allObjects] objectAtIndex:0];
if (!allowInput)
{
debug_printf("input disabled");
return ;
}
if ([touches count] > 1)
{
debug_printf("multiple - disabling input");
#ifdef ACTIVEGS
[[pManager getEmulatorView].contentView disableGestures:MODE_EMULATOR];
#endif
return ;
}
if (self.useTouch)
{
if (!self.secondTouch && zoomDisabled)
{
if (!mouseDown)
{
debug_printf("second touch mouse Down\n");
[self mouseDown];
}
else
{
debug_printf("second touch\n");
}
self.secondTouch = touch;
}
else
{
if (zoomDisabled)
{
debug_printf("third or more touch ignore");
}
else
{
debug_printf("ignoring second touch - too soon, must be a gestures");
#ifdef ACTIVEGS
[[pManager getEmulatorView].contentView disableGestures:MODE_EMULATOR];
#endif
}
}
return ;
}
self.useTouch = touch;
lastMousePos = [self rotateTouch:touch];
add_event_mouse(lastMousePos.x,lastMousePos.y,0,-1);
if (self.secondTouch)
{
// mouseDown déjà enclenchée -> on a fini
return ;
}
int nbtap = [touch tapCount];
if (nbtap==1)
{
// prépare la désactivation des gestures
//[[pManager getEmulatorView].contentView scheduleDisableAllGestures];
startNewPhase=TRUE;
}
if (nbtap==2)
{
extern void simulate_space_event();
simulate_space_event();
}
if (nbtap>=2 || g_adb.g_warp_pointer==WARP_TOUCHSCREEN)
{
debug_printf("*** multi-tap\n");
[self mouseUp];
[self mouseDown];
// Désactive immédiatement les gestures
#ifdef ACTIVEGS
if (g_adb.g_warp_pointer!=WARP_TOUCHSCREEN)
[[pManager getEmulatorView].contentView disableGestures:MODE_ALL];
#endif
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
debug_printf("touchesEnded : %d",[touches count]);
#ifdef ACTIVEGS
if (! [[pManager getEmulatorView].kbdc myTouchesEnded:touches])
{
// le pad n'est plus actif - reactive les input si elles étaient disabled
if (!allowInput)
[[pManager getEmulatorView].contentView reenableAllGestures];
}
#endif
if (!allowInput)
return ;
if ( [touches containsObject:self.useTouch]
&& [touches containsObject:self.secondTouch])
{
debug_printf("both keys ended at the sametime");
[self mouseUp];
self.useTouch = nil;
self.secondTouch = nil;
#ifdef ACTIVEGS
// vérifie les gestures
customView* cv = [pManager getEmulatorView].contentView;
[cv processGesturesOnTouchesEnded];
#endif
return ;
}
if ([touches containsObject:self.useTouch])
{
debug_printf("contains useTouch");
if (self.secondTouch)
{
// libère useTouch mais ne fait rien d'autre - maintien la touche enclenchée
debug_printf("maintaining mouseDown - secondTouch ");
self.useTouch = nil;
return;
}
else
{
debug_printf("touchesEnded\n");
[self mouseUp];
self.useTouch = nil;
#ifdef ACTIVEGS
// vérifie les gestures
customView* cv = [pManager getEmulatorView].contentView;
[cv processGesturesOnTouchesEnded];
#endif
}
}
if ([touches containsObject:self.secondTouch])
{
// correspond à un mouseUp
[self mouseUp];
self.secondTouch = nil;
debug_printf("secondTouch ended");
if (!self.useTouch)
{
#ifdef ACTIVEGS
// aucune touche n'est plus appuyée : réactive les gestures
[[pManager getEmulatorView].contentView scheduleEnableAllGestures];
#endif
}
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
// on est ici car lez zoom a démarré => théoriquement, il ne doit y avoir aucun useTouch, secondTouch, ou mouseDown
if ([touches containsObject:self.useTouch])
{
debug_printf("touchesCancelled useTouch\n");
self.useTouch = nil;
}
if ([touches containsObject:self.secondTouch])
{
debug_printf("touchesCancelled secondTouch\n");
self.secondTouch = nil;
}
// DEBUG INFO
if (mouseDown)
{
printf("mouseDown in touchesCancelled***");
[self mouseUp];
}
//[[pManager getEmulatorView].contentView reenableAllGestures];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
#ifdef ACTIVEGS
if ([[pManager getEmulatorView].kbdc myTouchesMoved:touches])
return ;
#endif
if (!allowInput)
return ;
if (startNewPhase)
{
debug_printf("first finger moved");
startNewPhase = FALSE;
#ifdef ACTIVEGS
[[pManager getEmulatorView].contentView disableGestures:MODE_ZOOM]; // à faire avant le schedule
[[pManager getEmulatorView].contentView scheduleDisableAllGestures];
#endif
}
if (!self.useTouch) return ;
if ([touches containsObject:self.useTouch])
{
lastMousePos = [self rotateTouch:self.useTouch];
add_event_mouse(lastMousePos.x,lastMousePos.y,mouseDown,1);
}
}
- (void)dealloc {
self.delegate = nil;
}
@end