2015-12-04 18:20:09 +01:00
|
|
|
/* 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.
|
|
|
|
*
|
2021-12-26 18:47:58 +01:00
|
|
|
* 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 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
2015-12-04 18:20:09 +01:00
|
|
|
*
|
|
|
|
* 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
|
2021-12-26 18:47:58 +01:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2015-12-04 18:20:09 +01:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Disable symbol overrides so that we can use system headers.
|
|
|
|
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
|
|
|
|
2016-01-07 12:40:55 +01:00
|
|
|
#include "backends/platform/ios7/ios7_osys_main.h"
|
|
|
|
#include "backends/platform/ios7/ios7_video.h"
|
2015-12-04 18:20:09 +01:00
|
|
|
|
2022-12-21 20:38:43 +00:00
|
|
|
#include "graphics/blit.h"
|
2016-01-07 12:40:55 +01:00
|
|
|
#include "backends/platform/ios7/ios7_app_delegate.h"
|
2015-12-04 18:20:09 +01:00
|
|
|
|
2023-01-07 17:53:38 +01:00
|
|
|
#define UIViewParentController(__view) ({ \
|
|
|
|
UIResponder *__responder = __view; \
|
|
|
|
while ([__responder isKindOfClass:[UIView class]]) \
|
|
|
|
__responder = [__responder nextResponder]; \
|
|
|
|
(UIViewController *)__responder; \
|
|
|
|
})
|
2015-12-11 14:24:29 +01:00
|
|
|
|
2023-01-07 17:53:38 +01:00
|
|
|
static void displayAlert(void *ctx) {
|
|
|
|
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Fatal Error"
|
|
|
|
message:[NSString stringWithCString:(const char *)ctx encoding:NSUTF8StringEncoding]
|
|
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
2015-12-11 14:24:29 +01:00
|
|
|
|
2023-01-07 17:53:38 +01:00
|
|
|
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
|
|
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
OSystem_iOS7::sharedInstance()->quit();
|
|
|
|
abort();
|
|
|
|
}];
|
2015-12-11 14:24:29 +01:00
|
|
|
|
2023-01-07 17:53:38 +01:00
|
|
|
[alert addAction:defaultAction];
|
|
|
|
[UIViewParentController([iOS7AppDelegate iPhoneView]) presentViewController:alert animated:YES completion:nil];
|
2015-12-11 14:24:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void OSystem_iOS7::fatalError() {
|
2017-12-05 11:24:39 -06:00
|
|
|
if (_lastErrorMessage.size()) {
|
|
|
|
dispatch_async_f(dispatch_get_main_queue(), (void *)_lastErrorMessage.c_str(), displayAlert);
|
2015-12-11 14:24:29 +01:00
|
|
|
for(;;);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
OSystem::fatalError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-16 00:53:20 +01:00
|
|
|
void OSystem_iOS7::logMessage(LogMessageType::Type type, const char *message) {
|
|
|
|
FILE *output = 0;
|
|
|
|
|
|
|
|
if (type == LogMessageType::kInfo || type == LogMessageType::kDebug)
|
|
|
|
output = stdout;
|
|
|
|
else
|
|
|
|
output = stderr;
|
|
|
|
|
|
|
|
if (type == LogMessageType::kError) {
|
|
|
|
_lastErrorMessage = message;
|
|
|
|
NSString *messageString = [NSString stringWithUTF8String:message];
|
|
|
|
NSLog(@"%@", messageString);
|
|
|
|
}
|
|
|
|
|
|
|
|
fputs(message, output);
|
|
|
|
fflush(output);
|
|
|
|
}
|
|
|
|
|
2016-10-20 22:38:14 +01:00
|
|
|
void OSystem_iOS7::engineInit() {
|
|
|
|
EventsBaseBackend::engineInit();
|
|
|
|
// Prevent the device going to sleep during game play (and in particular cut scenes)
|
2018-09-13 22:57:19 +09:00
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
|
|
|
|
});
|
2023-01-21 23:24:33 +01:00
|
|
|
[[iOS7AppDelegate iPhoneView] setIsInGame:YES];
|
2016-10-20 22:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void OSystem_iOS7::engineDone() {
|
|
|
|
EventsBaseBackend::engineDone();
|
|
|
|
// Allow the device going to sleep if idle while in the Launcher
|
2018-09-13 22:57:19 +09:00
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
|
|
|
|
});
|
2023-01-21 23:24:33 +01:00
|
|
|
[[iOS7AppDelegate iPhoneView] setIsInGame:NO];
|
2016-10-20 22:38:14 +01:00
|
|
|
}
|
|
|
|
|
2015-12-07 11:34:11 +01:00
|
|
|
void OSystem_iOS7::initVideoContext() {
|
2015-12-08 11:04:04 +01:00
|
|
|
_videoContext = [[iOS7AppDelegate iPhoneView] getVideoContext];
|
2015-12-04 18:20:09 +01:00
|
|
|
}
|
|
|
|
|
2017-09-27 19:06:42 +02:00
|
|
|
static inline void execute_on_main_thread(void (^block)(void)) {
|
|
|
|
if ([NSThread currentThread] == [NSThread mainThread]) {
|
|
|
|
block();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
dispatch_sync(dispatch_get_main_queue(), block);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-07 11:34:11 +01:00
|
|
|
void OSystem_iOS7::updateOutputSurface() {
|
2017-09-27 19:06:42 +02:00
|
|
|
execute_on_main_thread(^ {
|
|
|
|
[[iOS7AppDelegate iPhoneView] initSurface];
|
|
|
|
});
|
2015-12-04 18:20:09 +01:00
|
|
|
}
|
|
|
|
|
2015-12-07 11:34:11 +01:00
|
|
|
void OSystem_iOS7::internUpdateScreen() {
|
2015-12-04 18:20:09 +01:00
|
|
|
if (_mouseNeedTextureUpdate) {
|
|
|
|
updateMouseTexture();
|
|
|
|
_mouseNeedTextureUpdate = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (_dirtyRects.size()) {
|
|
|
|
Common::Rect dirtyRect = _dirtyRects.remove_at(_dirtyRects.size() - 1);
|
|
|
|
|
|
|
|
//printf("Drawing: (%i, %i) -> (%i, %i)\n", dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
|
|
|
|
drawDirtyRect(dirtyRect);
|
|
|
|
// TODO: Implement dirty rect code
|
|
|
|
//updateHardwareSurfaceForRect(dirtyRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_videoContext->overlayVisible) {
|
|
|
|
// TODO: Implement dirty rect code
|
|
|
|
_dirtyOverlayRects.clear();
|
|
|
|
/*while (_dirtyOverlayRects.size()) {
|
|
|
|
Common::Rect dirtyRect = _dirtyOverlayRects.remove_at(_dirtyOverlayRects.size() - 1);
|
|
|
|
|
|
|
|
//printf("Drawing: (%i, %i) -> (%i, %i)\n", dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
|
|
|
|
drawDirtyOverlayRect(dirtyRect);
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-07 11:34:11 +01:00
|
|
|
void OSystem_iOS7::drawDirtyRect(const Common::Rect &dirtyRect) {
|
2015-12-04 18:20:09 +01:00
|
|
|
// We only need to do a color look up for CLUT8
|
|
|
|
if (_framebuffer.format.bytesPerPixel != 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int h = dirtyRect.bottom - dirtyRect.top;
|
|
|
|
int w = dirtyRect.right - dirtyRect.left;
|
|
|
|
|
|
|
|
const byte *src = (const byte *)_framebuffer.getBasePtr(dirtyRect.left, dirtyRect.top);
|
|
|
|
byte *dstRaw = (byte *)_videoContext->screenTexture.getBasePtr(dirtyRect.left, dirtyRect.top);
|
|
|
|
|
|
|
|
// When we use CLUT8 do a color look up
|
|
|
|
for (int y = h; y > 0; y--) {
|
|
|
|
uint16 *dst = (uint16 *)dstRaw;
|
|
|
|
for (int x = w; x > 0; x--)
|
|
|
|
*dst++ = _gamePalette[*src++];
|
|
|
|
|
|
|
|
dstRaw += _videoContext->screenTexture.pitch;
|
|
|
|
src += _framebuffer.pitch - w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-14 11:39:24 +01:00
|
|
|
void OSystem_iOS7::virtualController(bool connect) {
|
|
|
|
execute_on_main_thread(^ {
|
|
|
|
[[iOS7AppDelegate iPhoneView] virtualController:connect];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-12-07 11:34:11 +01:00
|
|
|
void OSystem_iOS7::dirtyFullScreen() {
|
2015-12-04 18:20:09 +01:00
|
|
|
if (!_fullScreenIsDirty) {
|
|
|
|
_dirtyRects.clear();
|
|
|
|
_dirtyRects.push_back(Common::Rect(0, 0, _videoContext->screenWidth, _videoContext->screenHeight));
|
|
|
|
_fullScreenIsDirty = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-07 11:34:11 +01:00
|
|
|
void OSystem_iOS7::dirtyFullOverlayScreen() {
|
2015-12-04 18:20:09 +01:00
|
|
|
if (!_fullScreenOverlayIsDirty) {
|
|
|
|
_dirtyOverlayRects.clear();
|
|
|
|
_dirtyOverlayRects.push_back(Common::Rect(0, 0, _videoContext->overlayWidth, _videoContext->overlayHeight));
|
|
|
|
_fullScreenOverlayIsDirty = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-07 11:34:11 +01:00
|
|
|
void OSystem_iOS7::updateMouseTexture() {
|
2022-03-06 09:19:54 +01:00
|
|
|
int texWidth = getSizeNextPOT(_videoContext->mouseWidth);
|
|
|
|
int texHeight = getSizeNextPOT(_videoContext->mouseHeight);
|
2015-12-04 18:20:09 +01:00
|
|
|
|
|
|
|
Graphics::Surface &mouseTexture = _videoContext->mouseTexture;
|
|
|
|
if (mouseTexture.w != texWidth || mouseTexture.h != texHeight)
|
2021-05-06 10:45:34 +01:00
|
|
|
mouseTexture.create(texWidth, texHeight, Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0));
|
2015-12-04 18:20:09 +01:00
|
|
|
|
|
|
|
if (_mouseBuffer.format.bytesPerPixel == 1) {
|
|
|
|
const uint16 *palette;
|
|
|
|
if (_mouseCursorPaletteEnabled)
|
|
|
|
palette = _mouseCursorPalette;
|
|
|
|
else
|
|
|
|
palette = _gamePaletteRGBA5551;
|
|
|
|
|
|
|
|
uint16 *mouseBuf = (uint16 *)mouseTexture.getPixels();
|
|
|
|
for (uint x = 0; x < _videoContext->mouseWidth; ++x) {
|
|
|
|
for (uint y = 0; y < _videoContext->mouseHeight; ++y) {
|
|
|
|
const byte color = *(const byte *)_mouseBuffer.getBasePtr(x, y);
|
|
|
|
if (color != _mouseKeyColor)
|
|
|
|
mouseBuf[y * texWidth + x] = palette[color] | 0x1;
|
|
|
|
else
|
|
|
|
mouseBuf[y * texWidth + x] = 0x0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (crossBlit((byte *)mouseTexture.getPixels(), (const byte *)_mouseBuffer.getPixels(), mouseTexture.pitch,
|
|
|
|
_mouseBuffer.pitch, _mouseBuffer.w, _mouseBuffer.h, mouseTexture.format, _mouseBuffer.format)) {
|
2021-05-24 23:40:36 +01:00
|
|
|
// Apply color keying
|
|
|
|
const uint8 * src = (const uint8 *)_mouseBuffer.getPixels();
|
|
|
|
int srcBpp = _mouseBuffer.format.bytesPerPixel;
|
|
|
|
|
2020-10-11 03:56:11 +01:00
|
|
|
uint8 *dstRaw = (uint8 *)mouseTexture.getPixels();
|
|
|
|
|
2022-03-06 09:19:54 +01:00
|
|
|
for (int y = 0; y < _mouseBuffer.h; ++y, dstRaw += mouseTexture.pitch) {
|
2020-10-11 03:56:11 +01:00
|
|
|
uint16 *dst = (uint16 *)dstRaw;
|
2022-03-06 09:19:54 +01:00
|
|
|
for (int x = 0; x < _mouseBuffer.w; ++x, ++dst, src += srcBpp) {
|
2021-05-24 23:40:36 +01:00
|
|
|
if (
|
|
|
|
(srcBpp == 2 && *((const uint16*)src) == _mouseKeyColor) ||
|
|
|
|
(srcBpp == 4 && *((const uint32*)src) == _mouseKeyColor)
|
|
|
|
)
|
2020-10-11 03:56:11 +01:00
|
|
|
*dst &= ~1;
|
2015-12-04 18:20:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// TODO: Log this!
|
|
|
|
// Make the cursor all transparent... we really need a better fallback ;-).
|
|
|
|
memset(mouseTexture.getPixels(), 0, mouseTexture.h * mouseTexture.pitch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-27 19:06:42 +02:00
|
|
|
execute_on_main_thread(^ {
|
|
|
|
[[iOS7AppDelegate iPhoneView] updateMouseCursor];
|
|
|
|
});
|
2015-12-04 18:20:09 +01:00
|
|
|
}
|
2019-02-12 23:48:14 +00:00
|
|
|
|
|
|
|
void OSystem_iOS7::setShowKeyboard(bool show) {
|
|
|
|
if (show) {
|
2023-01-01 23:06:51 +01:00
|
|
|
#if TARGET_OS_IOS
|
2019-02-12 23:48:14 +00:00
|
|
|
execute_on_main_thread(^ {
|
|
|
|
[[iOS7AppDelegate iPhoneView] showKeyboard];
|
|
|
|
});
|
2023-01-01 23:06:51 +01:00
|
|
|
#elif TARGET_OS_TV
|
|
|
|
// Delay the showing of keyboard 1 second so the user
|
|
|
|
// is able to see the message
|
|
|
|
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
|
|
|
|
dispatch_after(delay, dispatch_get_main_queue(), ^(void){
|
|
|
|
[[iOS7AppDelegate iPhoneView] showKeyboard];
|
|
|
|
});
|
|
|
|
#endif
|
2019-02-12 23:48:14 +00:00
|
|
|
} else {
|
|
|
|
// Do not hide the keyboard in portrait mode as it is shown automatically and not
|
|
|
|
// just when asked with the kFeatureVirtualKeyboard.
|
|
|
|
if (_screenOrientation == kScreenOrientationLandscape || _screenOrientation == kScreenOrientationFlippedLandscape) {
|
|
|
|
execute_on_main_thread(^ {
|
|
|
|
[[iOS7AppDelegate iPhoneView] hideKeyboard];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OSystem_iOS7::isKeyboardShown() const {
|
2023-01-19 10:08:43 +01:00
|
|
|
__block bool isShown = false;
|
|
|
|
execute_on_main_thread(^{
|
|
|
|
isShown = [[iOS7AppDelegate iPhoneView] isKeyboardShown];
|
|
|
|
});
|
|
|
|
return isShown;
|
2019-02-12 23:48:14 +00:00
|
|
|
}
|
2023-05-31 08:02:38 +02:00
|
|
|
|
|
|
|
uint OSystem_iOS7::createOpenGLContext() {
|
2023-06-02 09:57:55 +02:00
|
|
|
return [[iOS7AppDelegate iPhoneView] createOpenGLContext];
|
2023-05-31 08:02:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void OSystem_iOS7::destroyOpenGLContext() {
|
2023-06-02 09:57:55 +02:00
|
|
|
[[iOS7AppDelegate iPhoneView] destroyOpenGLContext];
|
2023-05-31 08:02:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void OSystem_iOS7::refreshScreen() const {
|
2023-06-02 09:57:55 +02:00
|
|
|
[[iOS7AppDelegate iPhoneView] refreshScreen];
|
2023-05-31 08:02:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int OSystem_iOS7::getScreenWidth() const {
|
2023-06-02 09:57:55 +02:00
|
|
|
return [[iOS7AppDelegate iPhoneView] getScreenWidth];
|
2023-05-31 08:02:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int OSystem_iOS7::getScreenHeight() const {
|
2023-06-02 09:57:55 +02:00
|
|
|
return [[iOS7AppDelegate iPhoneView] getScreenHeight];
|
2023-05-31 08:02:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
float OSystem_iOS7::getSystemHiDPIScreenFactor() const {
|
2023-06-02 09:57:55 +02:00
|
|
|
return [[UIScreen mainScreen] scale];
|
2023-05-31 08:02:38 +02:00
|
|
|
}
|
|
|
|
|