/* 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;hviewSize = 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