/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ * */ #include "iphone_video.h" #include "iphone_common.h" static iPhoneView *sharedInstance = nil; static int _width = 0; static int _height = 0; static int _fullWidth; static int _fullHeight; static CGRect _screenRect; static char* _textureBuffer = 0; static int _textureWidth = 0; static int _textureHeight = 0; NSLock* _lock = nil; static int _needsScreenUpdate = 0; static UITouch* _firstTouch = NULL; static UITouch* _secondTouch = NULL; // static long lastTick = 0; // static int frames = 0; bool iPhone_isHighResDevice() { return _fullHeight > 480; } void iPhone_updateScreen() { if (!_needsScreenUpdate) { _needsScreenUpdate = 1; [sharedInstance performSelectorOnMainThread:@selector(updateSurface) withObject:nil waitUntilDone: NO]; } } void iPhone_updateScreenRect(unsigned short* screen, int x1, int y1, int x2, int y2) { //[_lock lock]; int y; for (y = y1; y < y2; ++y) { memcpy(&_textureBuffer[(y * _textureWidth + x1 )* 2], &screen[y * _width + x1], (x2 - x1) * 2); } //[_lock unlock]; } void iPhone_initSurface(int width, int height) { _width = width; _height = height; [sharedInstance performSelectorOnMainThread:@selector(initSurface) withObject:nil waitUntilDone: YES]; } bool iPhone_fetchEvent(int *outEvent, float *outX, float *outY) { id event = [sharedInstance getEvent]; if (event == nil) { return false; } id type = [event objectForKey:@"type"]; if (type == nil) { printf("fetchEvent says: No type!\n"); return false; } *outEvent = [type intValue]; *outX = [[event objectForKey:@"x"] floatValue]; *outY = [[event objectForKey:@"y"] floatValue]; return true; } const char* iPhone_getDocumentsDir() { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; return [documentsDirectory UTF8String]; } bool getLocalMouseCoords(CGPoint *point) { if (point->x < _screenRect.origin.x || point->x >= _screenRect.origin.x + _screenRect.size.width || point->y < _screenRect.origin.y || point->y >= _screenRect.origin.y + _screenRect.size.height) { return false; } point->x = (point->x - _screenRect.origin.x) / _screenRect.size.width; point->y = (point->y - _screenRect.origin.y) / _screenRect.size.height; return true; } uint getSizeNextPOT(uint size) { if ((size & (size - 1)) || !size) { int log = 0; while (size >>= 1) ++log; size = (2 << log); } return size; } @implementation iPhoneView + (Class) layerClass { return [CAEAGLLayer class]; } - (id)initWithFrame:(struct CGRect)frame { [super initWithFrame: frame]; _fullWidth = frame.size.width; _fullHeight = frame.size.height; _screenLayer = nil; sharedInstance = self; _lock = [NSLock new]; _keyboardView = nil; _context = nil; _screenTexture = 0; return self; } -(void) dealloc { [super dealloc]; if (_keyboardView != nil) { [_keyboardView dealloc]; } if (_screenTexture) free(_textureBuffer); } - (void *)getSurface { return _screenSurface; } - (void)drawRect:(CGRect)frame { // if (lastTick == 0) { // lastTick = time(0); // } // // frames++; // if (time(0) > lastTick) { // lastTick = time(0); // printf("FPS: %i\n", frames); // frames = 0; // } } - (void)updateSurface { if (!_needsScreenUpdate) { return; } _needsScreenUpdate = 0; GLfloat vertices[] = { 0.0f + _heightOffset, 0.0f + _widthOffset, _visibleWidth - _heightOffset, 0.0f + _widthOffset, 0.0f + _heightOffset, _visibleHeight - _widthOffset, _visibleWidth - _heightOffset, _visibleHeight - _widthOffset }; float texWidth = _width / (float)_textureWidth; float texHeight = _height / (float)_textureHeight; const GLfloat texCoords[] = { texWidth, 0.0f, 0.0f, 0.0f, texWidth, texHeight, 0.0f, texHeight }; glVertexPointer(2, GL_FLOAT, 0, vertices); glTexCoordPointer(2, GL_FLOAT, 0, texCoords); //[_lock lock]; // Unfortunately we have to update the whole texture every frame, since glTexSubImage2D is actually slower in all cases // due to the iPhone internals having to convert the whole texture back from its internal format when used. // In the future we could use several tiled textures instead. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _textureWidth, _textureHeight, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, _textureBuffer); //[_lock unlock]; glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer); [_context presentRenderbuffer:GL_RENDERBUFFER_OES]; } - (void)initSurface { _textureWidth = getSizeNextPOT(_width); _textureHeight = getSizeNextPOT(_height); UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; //printf("Window: (%d, %d), Surface: (%d, %d), Texture(%d, %d)\n", _fullWidth, _fullHeight, _width, _height, _textureWidth, _textureHeight); if (_context == nil) { orientation = UIDeviceOrientationLandscapeRight; CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer; eaglLayer.opaque = YES; eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGB565, kEAGLDrawablePropertyColorFormat, nil]; _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; if (!_context || [EAGLContext setCurrentContext:_context]) { glGenFramebuffersOES(1, &_viewFramebuffer); glGenRenderbuffersOES(1, &_viewRenderbuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, _viewFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer); [_context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id)self.layer]; glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, _viewRenderbuffer); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &_backingWidth); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &_backingHeight); if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) { NSLog(@"Failed to make complete framebuffer object %x.", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); return; } glViewport(0, 0, _backingWidth, _backingHeight); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_TEXTURE_2D); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); } } glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (orientation == UIDeviceOrientationLandscapeRight) { glRotatef(-90, 0, 0, 1); } else if (orientation == UIDeviceOrientationLandscapeLeft) { glRotatef(90, 0, 0, 1); } else { glRotatef(180, 0, 0, 1); } glOrthof(0, _backingWidth, 0, _backingHeight, 0, 1); if (_screenTexture > 0) { glDeleteTextures(1, &_screenTexture); } glGenTextures(1, &_screenTexture); glBindTexture(GL_TEXTURE_2D, _screenTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); if (_textureBuffer) { free(_textureBuffer); } int textureSize = _textureWidth * _textureHeight * 2; _textureBuffer = (char*)malloc(textureSize); memset(_textureBuffer, 0, textureSize); glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer); // The color buffer is triple-buffered, so we clear it multiple times right away to avid doing any glClears later. int clearCount = 5; while (clearCount-- > 0) { glClear(GL_COLOR_BUFFER_BIT); [_context presentRenderbuffer:GL_RENDERBUFFER_OES]; } if (_keyboardView != nil) { [_keyboardView removeFromSuperview]; [[_keyboardView inputView] removeFromSuperview]; } if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight) { _visibleHeight = _backingHeight; _visibleWidth = _backingWidth; float ratioDifference = ((float)_height / (float)_width) / ((float)_fullWidth / (float)_fullHeight); int rectWidth, rectHeight; if (ratioDifference < 1.0f) { rectWidth = _fullWidth * ratioDifference; rectHeight = _fullHeight; _widthOffset = (_fullWidth - rectWidth) / 2; _heightOffset = 0; } else { rectWidth = _fullWidth; rectHeight = _fullHeight / ratioDifference; _heightOffset = (_fullHeight - rectHeight) / 2; _widthOffset = 0; } //printf("Rect: %i, %i, %i, %i\n", _widthOffset, _heightOffset, rectWidth, rectHeight); _screenRect = CGRectMake(_widthOffset, _heightOffset, rectWidth, rectHeight); } else { float ratio = (float)_height / (float)_width; int height = _fullWidth * ratio; //printf("Making rect (%u, %u)\n", _fullWidth, height); _screenRect = CGRectMake(0, 0, _fullWidth - 1, height - 1); _visibleHeight = height; _visibleWidth = _backingWidth; _heightOffset = 0.0f; _widthOffset = 0.0f; CGRect keyFrame = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f); if (_keyboardView == nil) { _keyboardView = [[SoftKeyboard alloc] initWithFrame:keyFrame]; [_keyboardView setInputDelegate:self]; } [self addSubview:[_keyboardView inputView]]; [self addSubview: _keyboardView]; [[_keyboardView inputView] becomeFirstResponder]; } } - (id)getEvent { if (_events == nil || [_events count] == 0) { return nil; } id event = [_events objectAtIndex: 0]; [_events removeObjectAtIndex: 0]; return event; } - (void)addEvent:(NSDictionary*)event { if (_events == nil) _events = [[NSMutableArray alloc] init]; [_events addObject: event]; } - (void)deviceOrientationChanged:(int)orientation { [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputOrientationChanged], @"type", [NSNumber numberWithFloat:(float)orientation], @"x", [NSNumber numberWithFloat:0], @"y", nil ] ]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSSet *allTouches = [event allTouches]; switch ([allTouches count]) { case 1: { UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self]; if (!getLocalMouseCoords(&point)) return; _firstTouch = touch; [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputMouseDown], @"type", [NSNumber numberWithFloat:point.x], @"x", [NSNumber numberWithFloat:point.y], @"y", nil ] ]; break; } case 2: { UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self]; if (!getLocalMouseCoords(&point)) return; _secondTouch = touch; [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputMouseSecondDown], @"type", [NSNumber numberWithFloat:point.x], @"x", [NSNumber numberWithFloat:point.y], @"y", nil ] ]; break; } } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { NSSet *allTouches = [event allTouches]; for (UITouch* touch in touches) { if (touch == _firstTouch) { CGPoint point = [touch locationInView:self]; if (!getLocalMouseCoords(&point)) return; [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputMouseDragged], @"type", [NSNumber numberWithFloat:point.x], @"x", [NSNumber numberWithFloat:point.y], @"y", nil ] ]; } else if (touch == _secondTouch) { CGPoint point = [touch locationInView:self]; if (!getLocalMouseCoords(&point)) return; [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputMouseSecondDragged], @"type", [NSNumber numberWithFloat:point.x], @"x", [NSNumber numberWithFloat:point.y], @"y", nil ] ]; } } } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { NSSet *allTouches = [event allTouches]; switch ([allTouches count]) { case 1: { UITouch *touch = [[allTouches allObjects] objectAtIndex:0]; CGPoint point = [touch locationInView:self]; if (!getLocalMouseCoords(&point)) return; [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputMouseUp], @"type", [NSNumber numberWithFloat:point.x], @"x", [NSNumber numberWithFloat:point.y], @"y", nil ] ]; break; } case 2: { UITouch *touch = [[allTouches allObjects] objectAtIndex:1]; CGPoint point = [touch locationInView:self]; if (!getLocalMouseCoords(&point)) return; [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputMouseSecondUp], @"type", [NSNumber numberWithFloat:point.x], @"x", [NSNumber numberWithFloat:point.y], @"y", nil ] ]; break; } } } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { } - (void)handleKeyPress:(unichar)c { [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputKeyPressed], @"type", [NSNumber numberWithFloat:(float)c], @"x", [NSNumber numberWithFloat:0], @"y", nil ] ]; } - (BOOL)canHandleSwipes { return TRUE; } - (int)swipe:(int)num withEvent:(struct __GSEvent *)event { //printf("swipe: %i\n", num); [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputSwipe], @"type", [NSNumber numberWithFloat:(float)num], @"x", [NSNumber numberWithFloat:0], @"y", nil ] ]; } - (void)applicationSuspend { [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputApplicationSuspended], @"type", [NSNumber numberWithFloat:0], @"x", [NSNumber numberWithFloat:0], @"y", nil ] ]; } - (void)applicationResume { [self addEvent: [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kInputApplicationResumed], @"type", [NSNumber numberWithFloat:0], @"x", [NSNumber numberWithFloat:0], @"y", nil ] ]; } @end