tidy up readme and fix IOS build

This commit is contained in:
UnknownShadow200 2024-07-13 09:21:21 +10:00
parent dbab0091b2
commit 649eeba2ab
5 changed files with 746 additions and 684 deletions

View File

@ -14,6 +14,9 @@
9A6C79652BFDDEF200676D27 /* FancyLighting.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A6C79642BFDDEF100676D27 /* FancyLighting.c */; };
9A6C79672BFDDF0700676D27 /* Queue.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A6C79662BFDDF0600676D27 /* Queue.c */; };
9A6C7DFA2C2F610C00676D27 /* LBackend_ios.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A6C7DF92C2F610C00676D27 /* LBackend_ios.m */; };
9A6C7DFC2C41E93700676D27 /* InputHandler.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A6C7DFB2C41E93700676D27 /* InputHandler.c */; };
9A6C7DFE2C41E95D00676D27 /* MenuOptions.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A6C7DFD2C41E95C00676D27 /* MenuOptions.c */; };
9A6C7E002C41EED700676D27 /* Window_ios.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A6C7DFF2C41EED700676D27 /* Window_ios.m */; };
9A7401D92B737D5C0040E575 /* Commands.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A7401D82B737D5B0040E575 /* Commands.c */; };
9A7401DB2B7384060040E575 /* SSL.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A7401DA2B7384060040E575 /* SSL.c */; };
9A89D4F227F802F600FF3F80 /* LWidgets.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A89D37827F802F500FF3F80 /* LWidgets.c */; };
@ -103,6 +106,9 @@
9A6C79642BFDDEF100676D27 /* FancyLighting.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = FancyLighting.c; sourceTree = "<group>"; };
9A6C79662BFDDF0600676D27 /* Queue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Queue.c; sourceTree = "<group>"; };
9A6C7DF92C2F610C00676D27 /* LBackend_ios.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LBackend_ios.m; sourceTree = "<group>"; };
9A6C7DFB2C41E93700676D27 /* InputHandler.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = InputHandler.c; sourceTree = "<group>"; };
9A6C7DFD2C41E95C00676D27 /* MenuOptions.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MenuOptions.c; sourceTree = "<group>"; };
9A6C7DFF2C41EED700676D27 /* Window_ios.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Window_ios.m; sourceTree = "<group>"; };
9A7401D82B737D5B0040E575 /* Commands.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Commands.c; sourceTree = "<group>"; };
9A7401DA2B7384060040E575 /* SSL.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SSL.c; sourceTree = "<group>"; };
9A89D35727F802B100FF3F80 /* ClassiCube.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClassiCube.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -218,6 +224,9 @@
9A89D37727F802F500FF3F80 /* src */ = {
isa = PBXGroup;
children = (
9A6C7DFF2C41EED700676D27 /* Window_ios.m */,
9A6C7DFD2C41E95C00676D27 /* MenuOptions.c */,
9A6C7DFB2C41E93700676D27 /* InputHandler.c */,
9A6C7DF92C2F610C00676D27 /* LBackend_ios.m */,
9A6C79662BFDDF0600676D27 /* Queue.c */,
9A6C79642BFDDEF100676D27 /* FancyLighting.c */,
@ -386,6 +395,7 @@
9A89D58327F802F600FF3F80 /* EntityComponents.c in Sources */,
9A7401D92B737D5C0040E575 /* Commands.c in Sources */,
9A89D57327F802F600FF3F80 /* Utils.c in Sources */,
9A6C7DFE2C41E95D00676D27 /* MenuOptions.c in Sources */,
9A89D58427F802F600FF3F80 /* Camera.c in Sources */,
9A89D57C27F802F600FF3F80 /* Chat.c in Sources */,
9A89D50527F802F600FF3F80 /* LScreens.c in Sources */,
@ -456,11 +466,13 @@
9A89D4FE27F802F600FF3F80 /* Inventory.c in Sources */,
9A89D56527F802F600FF3F80 /* Generator.c in Sources */,
9A89D57527F802F600FF3F80 /* AxisLinesRenderer.c in Sources */,
9A6C7E002C41EED700676D27 /* Window_ios.m in Sources */,
9A89D55E27F802F600FF3F80 /* World.c in Sources */,
9A89D58627F802F600FF3F80 /* Screens.c in Sources */,
9A89D4F927F802F600FF3F80 /* Http_Worker.c in Sources */,
9A89D58B27F802F600FF3F80 /* Launcher.c in Sources */,
9A89D59027F802F600FF3F80 /* Stream.c in Sources */,
9A6C7DFC2C41E93700676D27 /* InputHandler.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -73,7 +73,8 @@ And also runs on:
* IRIX - needs <code>curl</code> and <code>openal</code> packages
* SerenityOS - needs <code>SDL2</code>
* Classic Mac OS (System 7 and later)
* Dreamcast - unfinished, but renders (can [download from here](https://www.classicube.net/download/dreamcast))
* Dreamcast - unfinished, but usable (can [download from here](https://www.classicube.net/download/dreamcast))
* Saturn - unfinished, major rendering and **stability issues** (if you have a GitHub account, can [download from here](https://github.com/ClassiCube/ClassiCube/actions/workflows/build_saturn.yml))
* Switch - unfinished, but usable (can [download from here](https://www.classicube.net/download/switch))
* Wii U - unfinished, major issues (if you have a GitHub account, can [download from here](https://github.com/ClassiCube/ClassiCube/actions/workflows/build_wiiu.yml)), **untested on real hardware**)
* Wii - unfinished, but usable (can [download from here](https://www.classicube.net/download/wii))
@ -85,7 +86,7 @@ And also runs on:
* PSP - unfinished, rendering issues (can [download from here](https://www.classicube.net/download/psp))
* PS3 - unfinished, rendering issues (can [download from here](https://www.classicube.net/download/ps3))
* PS2 - unfinished, major rendering and **stability issues** (can [download from here](https://www.classicube.net/download/ps2))
* PS1 - unfinished, major rendering and **stability issues**
* PS1 - unfinished, major rendering and **stability issues** (if you have a GitHub account, can [download from here](https://github.com/ClassiCube/ClassiCube/actions/workflows/build_ps1.yml))
* Xbox 360 - completely unfinished (if you have a GitHub account, can [download from here](https://github.com/ClassiCube/ClassiCube/actions/workflows/build_xbox360.yml)), **untested on real hardware**)
* Xbox - unfinished, major rendering issues (can [download from here](https://www.classicube.net/download/xbox))
@ -139,24 +140,24 @@ Compiling with TCC:
Install appropriate libs as required. For ubuntu these are: libx11-dev, libxi-dev and libgl1-mesa-dev
```gcc -fno-math-errno *.c -o ClassiCube -rdynamic -lpthread -lX11 -lXi -lGL -ldl```
```gcc -fno-math-errno src/*.c -o ClassiCube -rdynamic -lpthread -lX11 -lXi -lGL -ldl```
##### Cross compiling for Windows (32 bit):
```i686-w64-mingw32-gcc -fno-math-errno *.c -o ClassiCube.exe -mwindows -lwinmm -limagehlp```
```i686-w64-mingw32-gcc -fno-math-errno src/*.c -o ClassiCube.exe -mwindows -lwinmm -limagehlp```
##### Cross compiling for Windows (64 bit):
```x86_64-w64-mingw32-gcc -fno-math-errno *.c -o ClassiCube.exe -mwindows -lwinmm -limagehlp```
```x86_64-w64-mingw32-gcc -fno-math-errno src/*.c -o ClassiCube.exe -mwindows -lwinmm -limagehlp```
##### Raspberry Pi
Although the regular linux compiliation flags will work fine, to take full advantage of the hardware:
```gcc -fno-math-errno *.c -o ClassiCube -DCC_BUILD_RPI -rdynamic -lpthread -lX11 -lXi -lEGL -lGLESv2 -ldl```
```gcc -fno-math-errno src/*.c -o ClassiCube -DCC_BUILD_RPI -rdynamic -lpthread -lX11 -lXi -lEGL -lGLESv2 -ldl```
## Compiling - macOS
```cc -fno-math-errno *.c *.m -o ClassiCube -framework Cocoa -framework OpenGL -framework IOKit -lobjc```
```cc -fno-math-errno src/*.c src/*.m -o ClassiCube -framework Cocoa -framework OpenGL -framework IOKit -lobjc```
Note: You may need to install Xcode before you can compile ClassiCube
@ -192,7 +193,7 @@ Open the `ios/CCIOS.xcodeproj` project in Xcode, and then compile it
## Compiling - webclient
```emcc *.c -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_STACK=1Mb --js-library interop_web.js```
```emcc src/*.c -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_STACK=1Mb --js-library interop_web.js```
The generated javascript file has some issues. [See here for how to fix](doc/compile-fixes.md#webclient-patches)
@ -314,37 +315,37 @@ Run `make saturn`. You'll need [libyaul](https://github.com/yaul-org/libyaul)
Install libxi, libexecinfo, curl and openal-soft package if needed
```cc *.c -o ClassiCube -I /usr/local/include -L /usr/local/lib -lm -lpthread -lX11 -lXi -lGL -lexecinfo```
```cc src/*.c -o ClassiCube -I /usr/local/include -L /usr/local/lib -lm -lpthread -lX11 -lXi -lGL -lexecinfo```
#### OpenBSD
Install libexecinfo, curl and openal package if needed
```cc *.c -o ClassiCube -I /usr/X11R6/include -I /usr/local/include -L /usr/X11R6/lib -L /usr/local/lib -lm -lpthread -lX11 -lXi -lGL -lexecinfo```
```cc src/*.c -o ClassiCube -I /usr/X11R6/include -I /usr/local/include -L /usr/X11R6/lib -L /usr/local/lib -lm -lpthread -lX11 -lXi -lGL -lexecinfo```
#### NetBSD
Install libexecinfo, curl and openal-soft package if needed
```cc *.c -o ClassiCube -I /usr/X11R7/include -I /usr/pkg/include -L /usr/X11R7/lib -L /usr/pkg/lib -lpthread -lX11 -lXi -lGL -lexecinfo```
```cc src/*.c -o ClassiCube -I /usr/X11R7/include -I /usr/pkg/include -L /usr/X11R7/lib -L /usr/pkg/lib -lpthread -lX11 -lXi -lGL -lexecinfo```
#### DragonflyBSD
```cc *.c -o ClassiCube -I /usr/local/include -L /usr/local/lib -lm -lpthread -lX11 -lXi -lGL -lexecinfo```
```cc src/*.c -o ClassiCube -I /usr/local/include -L /usr/local/lib -lm -lpthread -lX11 -lXi -lGL -lexecinfo```
#### Solaris
```gcc -fno-math-errno *.c -o ClassiCube -lsocket -lX11 -lXi -lGL```
```gcc -fno-math-errno src/*.c -o ClassiCube -lsocket -lX11 -lXi -lGL```
#### Haiku
Install openal_devel package if needed
```cc -fno-math-errno *.c interop_BeOS.cpp -o ClassiCube -lGL -lnetwork -lstdc++ -lbe -lgame -ltracker```
```cc -fno-math-errno src/*.c interop_BeOS.cpp -o ClassiCube -lGL -lnetwork -lstdc++ -lbe -lgame -ltracker```
#### BeOS
```cc -fno-math-errno *.c interop_BeOS.cpp -o ClassiCube -lGL -lbe -lgame -ltracker```
```cc -fno-math-errno src/*.c interop_BeOS.cpp -o ClassiCube -lGL -lbe -lgame -ltracker```
#### IRIX
@ -354,7 +355,7 @@ Install openal_devel package if needed
Install SDL2 port if needed
```cc *.c -o ClassiCube -lgl -lSDL2```
```cc src/*.c -o ClassiCube -lgl -lSDL2```
#### Classic Mac OS

View File

@ -39,7 +39,7 @@ void LInput_SetPlaceholder(UITextField* fld, const char* placeholder);
/*########################################################################################################################*
*------------------------------------------------------Common helpers--------------------------------------------------------*
*----------------------------------------------------Common helpers------------------------------------------------------*
*#########################################################################################################################*/
static NSMutableAttributedString* ToAttributedString(const cc_string* text) {
// NSMutableAttributedString - iOS 3.2

708
src/Window_ios.m Normal file
View File

@ -0,0 +1,708 @@
// Silence deprecation warnings on modern iOS
#define GLES_SILENCE_DEPRECATION
#include "Core.h"
#if defined CC_BUILD_IOS
#include "_WindowBase.h"
#include "Bitmap.h"
#include "Input.h"
#include "Platform.h"
#include "String.h"
#include "Errors.h"
#include "Drawer2D.h"
#include "Launcher.h"
#include "Funcs.h"
#include "Gui.h"
#include <mach-o/dyld.h>
#include <sys/stat.h>
#include <UIKit/UIKit.h>
#include <UIKit/UIPasteboard.h>
#include <CoreText/CoreText.h>
#ifdef TARGET_OS_TV
// NSFontAttributeName etc - iOS 6.0
#define TEXT_ATTRIBUTE_FONT NSFontAttributeName
#define TEXT_ATTRIBUTE_COLOR NSForegroundColorAttributeName
#else
// UITextAttributeFont etc - iOS 5.0
#define TEXT_ATTRIBUTE_FONT UITextAttributeFont
#define TEXT_ATTRIBUTE_COLOR UITextAttributeTextColor
#endif
// shared state with LBackend_ios.m and interop_ios.m
UITextField* kb_widget;
CGContextRef win_ctx;
UIView* view_handle;
UIViewController* cc_controller;
UIColor* ToUIColor(BitmapCol color, float A);
NSString* ToNSString(const cc_string* text);
void LInput_SetKeyboardType(UITextField* fld, int flags);
void LInput_SetPlaceholder(UITextField* fld, const char* placeholder);
UIInterfaceOrientationMask SupportedOrientations(void);
void LogUnhandledNSErrors(NSException* ex);
@interface CCWindow : UIWindow
@end
@interface CCViewController : UIViewController<UIDocumentPickerDelegate, UIAlertViewDelegate>
@end
static UIWindow* win_handle;
static cc_bool launcherMode;
static void AddTouch(UITouch* t) {
CGPoint loc = [t locationInView:view_handle];
int x = loc.x, y = loc.y; long ui_id = (long)t;
Platform_Log3("POINTER %x - DOWN %i,%i", &ui_id, &x, &y);
Input_AddTouch((long)t, loc.x, loc.y);
}
static void UpdateTouch(UITouch* t) {
CGPoint loc = [t locationInView:view_handle];
int x = loc.x, y = loc.y; long ui_id = (long)t;
Platform_Log3("POINTER %x - MOVE %i,%i", &ui_id, &x, &y);
Input_UpdateTouch((long)t, loc.x, loc.y);
}
static void RemoveTouch(UITouch* t) {
CGPoint loc = [t locationInView:view_handle];
int x = loc.x, y = loc.y; long ui_id = (long)t;
Platform_Log3("POINTER %x - UP %i,%i", &ui_id, &x, &y);
Input_RemoveTouch((long)t, loc.x, loc.y);
}
static cc_bool landscape_locked;
UIInterfaceOrientationMask SupportedOrientations(void) {
if (landscape_locked)
return UIInterfaceOrientationMaskLandscape;
return UIInterfaceOrientationMaskAll;
}
static cc_bool fullscreen = true;
static void UpdateStatusBar(void) {
if ([cc_controller respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
// setNeedsStatusBarAppearanceUpdate - iOS 7.0
[cc_controller setNeedsStatusBarAppearanceUpdate];
} else {
[[UIApplication sharedApplication] setStatusBarHidden:fullscreen withAnimation:UIStatusBarAnimationNone];
}
}
static CGRect GetViewFrame(void) {
UIScreen* screen = UIScreen.mainScreen;
return fullscreen ? screen.bounds : screen.applicationFrame;
}
@implementation CCWindow
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event {
// touchesBegan:withEvent - iOS 2.0
for (UITouch* t in touches) AddTouch(t);
// clicking on the background should dismiss onscren keyboard
if (launcherMode) { [view_handle endEditing:NO]; }
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent *)event {
// touchesMoved:withEvent - iOS 2.0
for (UITouch* t in touches) UpdateTouch(t);
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent *)event {
// touchesEnded:withEvent - iOS 2.0
for (UITouch* t in touches) RemoveTouch(t);
}
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent *)event {
// touchesCancelled:withEvent - iOS 2.0
for (UITouch* t in touches) RemoveTouch(t);
}
- (BOOL)isOpaque { return YES; }
@end
@implementation CCViewController
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
// supportedInterfaceOrientations - iOS 6.0
return SupportedOrientations();
}
- (BOOL)shouldAutorotate {
// shouldAutorotate - iOS 6.0
return YES;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)ori {
// shouldAutorotateToInterfaceOrientation - iOS 2.0
if (landscape_locked && !(ori == UIInterfaceOrientationLandscapeLeft || ori == UIInterfaceOrientationLandscapeRight))
return NO;
return YES;
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator {
// viewWillTransitionToSize:withTransitionCoordinator - iOS 8.0
Window_Main.Width = size.width;
Window_Main.Height = size.height;
Event_RaiseVoid(&WindowEvents.Resized);
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
// ==== UIDocumentPickerDelegate ====
static FileDialogCallback open_dlg_callback;
static char save_buffer[FILENAME_SIZE];
static cc_string save_path = String_FromArray(save_buffer);
static void DeleteExportTempFile(void) {
if (!save_path.length) return;
char path[NATIVE_STR_LEN];
String_EncodeUtf8(path, &save_path);
unlink(path);
save_path.length = 0;
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
// documentPicker:didPickDocumentAtURL - iOS 8.0
NSString* str = url.path;
const char* utf8 = str.UTF8String;
char tmpBuffer[NATIVE_STR_LEN];
cc_string tmp = String_FromArray(tmpBuffer);
String_AppendUtf8(&tmp, utf8, String_Length(utf8));
DeleteExportTempFile();
if (!open_dlg_callback) return;
open_dlg_callback(&tmp);
open_dlg_callback = NULL;
}
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
// documentPickerWasCancelled - iOS 8.0
DeleteExportTempFile();
}
static cc_bool kb_active;
- (void)keyboardDidShow:(NSNotification*)notification {
NSDictionary* info = notification.userInfo;
if (kb_active) return;
// TODO this is wrong
// TODO this doesn't actually trigger view resize???
kb_active = true;
double interval = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
NSInteger curve = [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect winFrame = view_handle.frame;
cc_bool can_shift = true;
// would the active input widget be pushed offscreen?
if (kb_widget) {
can_shift = kb_widget.frame.origin.y > kbFrame.size.height;
}
if (can_shift) winFrame.origin.y = -kbFrame.size.height;
kb_widget = nil;
Platform_LogConst("APPEAR");
[UIView animateWithDuration:interval delay: 0.0 options:curve animations:^{
view_handle.frame = winFrame;
} completion:nil];
}
- (void)keyboardDidHide:(NSNotification*)notification {
NSDictionary* info = notification.userInfo;
if (!kb_active) return;
kb_active = false;
kb_widget = nil;
double interval = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
NSInteger curve = [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
CGRect winFrame = view_handle.frame;
winFrame.origin.y = 0;
Platform_LogConst("VANISH");
[UIView animateWithDuration:interval delay: 0.0 options:curve animations:^{
view_handle.frame = winFrame;
} completion:nil];
}
- (BOOL)prefersStatusBarHidden {
// prefersStatusBarHidden - iOS 7.0
return fullscreen;
}
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
// preferredScreenEdgesDeferringSystemGestures - iOS 11.0
// recent iOS versions have a 'bottom home bar', which when swiped up,
// switches out of ClassiCube and to the app list menu
// overriding this forces the user to swipe up twice, which should
// significantly the chance of accidentally triggering this gesture
return UIRectEdgeBottom;
}
// == UIAlertViewDelegate ==
static int alert_completed;
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
alert_completed = true;
}
@end
// iOS textfields manage ctrl+c/v
void Clipboard_GetText(cc_string* value) { }
void Clipboard_SetText(const cc_string* value) { }
/*########################################################################################################################*
*---------------------------------------------------------Window----------------------------------------------------------*
*#########################################################################################################################*/
// no cursor on iOS
void Cursor_GetRawPos(int* x, int* y) { *x = 0; *y = 0; }
void Cursor_SetPosition(int x, int y) { }
void Cursor_DoSetVisible(cc_bool visible) { }
void Window_SetTitle(const cc_string* title) {
// TODO: Implement this somehow
}
void Window_PreInit(void) {
DisplayInfo.CursorVisible = true;
}
void Window_Init(void) {
//Window_Main.SoftKeyboard = SOFT_KEYBOARD_RESIZE;
// keyboard now shifts up
Window_Main.SoftKeyboard = SOFT_KEYBOARD_SHIFT;
Input_SetTouchMode(true);
Input.Sources = INPUT_SOURCE_NORMAL;
Gui_SetTouchUI(true);
DisplayInfo.Depth = 32;
DisplayInfo.ScaleX = 1; // TODO dpi scale
DisplayInfo.ScaleY = 1; // TODO dpi scale
NSSetUncaughtExceptionHandler(LogUnhandledNSErrors);
}
void Window_Free(void) { }
static UIColor* CalcBackgroundColor(void) {
// default to purple if no themed background color yet
if (!Launcher_Theme.BackgroundColor)
return UIColor.purpleColor;
return ToUIColor(Launcher_Theme.BackgroundColor, 1.0f);
}
static CGRect DoCreateWindow(void) {
// UIKeyboardWillShowNotification - iOS 2.0
cc_controller = [CCViewController alloc];
UpdateStatusBar();
CGRect bounds = GetViewFrame();
win_handle = [[CCWindow alloc] initWithFrame:bounds];
win_handle.rootViewController = cc_controller;
win_handle.backgroundColor = CalcBackgroundColor();
Window_Main.Exists = true;
Window_Main.UIScaleX = DEFAULT_UI_SCALE_X;
Window_Main.UIScaleY = DEFAULT_UI_SCALE_Y;
Window_Main.Width = bounds.size.width;
Window_Main.Height = bounds.size.height;
Window_Main.SoftKeyboardInstant = true;
NSNotificationCenter* notifications = NSNotificationCenter.defaultCenter;
[notifications addObserver:cc_controller selector:@selector(keyboardDidShow:) name:UIKeyboardWillShowNotification object:nil];
[notifications addObserver:cc_controller selector:@selector(keyboardDidHide:) name:UIKeyboardWillHideNotification object:nil];
return bounds;
}
void Window_SetSize(int width, int height) { }
void Window_Show(void) {
[win_handle makeKeyAndVisible];
}
void Window_RequestClose(void) {
Event_RaiseVoid(&WindowEvents.Closing);
}
void Window_ProcessEvents(float delta) {
SInt32 res;
// manually tick event queue
do {
res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE);
} while (res == kCFRunLoopRunHandledSource);
}
void Gamepads_Init(void) {
}
void Gamepads_Process(float delta) { }
void ShowDialogCore(const char* title, const char* msg) {
// UIAlertController - iOS 8.0
// UIAlertAction - iOS 8.0
// UIAlertView - iOS 2.0
Platform_LogConst(title);
Platform_LogConst(msg);
NSString* _title = [NSString stringWithCString:title encoding:NSASCIIStringEncoding];
NSString* _msg = [NSString stringWithCString:msg encoding:NSASCIIStringEncoding];
alert_completed = false;
#ifdef TARGET_OS_TV
UIAlertController* alert = [UIAlertController alertControllerWithTitle:_title message:_msg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* okBtn = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* act) { alert_completed = true; }];
[alert addAction:okBtn];
[cc_controller presentViewController:alert animated:YES completion: Nil];
#else
UIAlertView* alert = [UIAlertView alloc];
alert = [alert initWithTitle:_title message:_msg delegate:cc_controller cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
#endif
// TODO clicking outside message box crashes launcher
// loop until alert is closed TODO avoid sleeping
while (!alert_completed) {
Window_ProcessEvents(0.0);
Thread_Sleep(16);
}
}
@interface CCKBController : NSObject<UITextFieldDelegate>
@end
@implementation CCKBController
- (void)handleTextChanged:(id)sender {
UITextField* src = (UITextField*)sender;
const char* str = src.text.UTF8String;
char tmpBuffer[NATIVE_STR_LEN];
cc_string tmp = String_FromArray(tmpBuffer);
String_AppendUtf8(&tmp, str, String_Length(str));
Event_RaiseString(&InputEvents.TextChanged, &tmp);
}
// === UITextFieldDelegate ===
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
// textFieldShouldReturn - iOS 2.0
Input_SetPressed(CCKEY_ENTER);
Input_SetReleased(CCKEY_ENTER);
return YES;
}
@end
static UITextField* text_input;
static CCKBController* kb_controller;
void OnscreenKeyboard_Open(struct OpenKeyboardArgs* args) {
if (!kb_controller) {
kb_controller = [[CCKBController alloc] init];
CFBridgingRetain(kb_controller); // prevent GC TODO even needed?
}
DisplayInfo.ShowingSoftKeyboard = true;
text_input = [[UITextField alloc] initWithFrame:CGRectZero];
text_input.hidden = YES;
text_input.delegate = kb_controller;
[text_input addTarget:kb_controller action:@selector(handleTextChanged:) forControlEvents:UIControlEventEditingChanged];
LInput_SetKeyboardType(text_input, args->type);
LInput_SetPlaceholder(text_input, args->placeholder);
[view_handle addSubview:text_input];
[text_input becomeFirstResponder];
}
void OnscreenKeyboard_SetText(const cc_string* text) {
NSString* str = ToNSString(text);
NSString* cur = text_input.text;
// otherwise on iOS 5, this causes an infinite loop
if (cur && [str isEqualToString:cur]) return;
text_input.text = str;
}
void OnscreenKeyboard_Close(void) {
DisplayInfo.ShowingSoftKeyboard = false;
[text_input resignFirstResponder];
}
int Window_GetWindowState(void) {
return fullscreen ? WINDOW_STATE_FULLSCREEN : WINDOW_STATE_NORMAL;
}
static void ToggleFullscreen(cc_bool isFullscreen) {
fullscreen = isFullscreen;
UpdateStatusBar();
view_handle.frame = GetViewFrame();
}
cc_result Window_EnterFullscreen(void) {
ToggleFullscreen(true); return 0;
}
cc_result Window_ExitFullscreen(void) {
ToggleFullscreen(false); return 0;
}
int Window_IsObscured(void) { return 0; }
void Window_EnableRawMouse(void) { DefaultEnableRawMouse(); }
void Window_UpdateRawMouse(void) { }
void Window_DisableRawMouse(void) { DefaultDisableRawMouse(); }
void Window_LockLandscapeOrientation(cc_bool lock) {
// attemptRotationToDeviceOrientation - iOS 5.0
// TODO doesn't work properly.. setting 'UIInterfaceOrientationUnknown' apparently
// restores orientation, but doesn't actually do that when I tried it
if (lock) {
//NSInteger ori = lock ? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationUnknown;
NSInteger ori = UIInterfaceOrientationLandscapeRight;
UIDevice* device = UIDevice.currentDevice;
NSNumber* value = [NSNumber numberWithInteger:ori];
[device setValue:value forKey:@"orientation"];
}
landscape_locked = lock;
[UIViewController attemptRotationToDeviceOrientation];
}
cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) {
// UIDocumentPickerViewController - iOS 8.0
// see the custom UTITypes declared in Info.plist
NSDictionary* fileExt_map =
@{
@".cw" : @"com.classicube.client.ios-cw",
@".dat" : @"com.classicube.client.ios-dat",
@".lvl" : @"com.classicube.client.ios-lvl",
@".fcm" : @"com.classicube.client.ios-fcm",
@".zip" : @"public.zip-archive"
};
NSMutableArray* types = [NSMutableArray array];
const char* const* filters = args->filters;
for (int i = 0; filters[i]; i++)
{
NSString* fileExt = [NSString stringWithUTF8String:filters[i]];
NSString* utType = [fileExt_map objectForKey:fileExt];
if (utType) [types addObject:utType];
}
UIDocumentPickerViewController* dlg;
dlg = [UIDocumentPickerViewController alloc];
dlg = [dlg initWithDocumentTypes:types inMode:UIDocumentPickerModeOpen];
//dlg = [dlg initWithDocumentTypes:types inMode:UIDocumentPickerModeImport];
open_dlg_callback = args->Callback;
dlg.delegate = cc_controller;
[cc_controller presentViewController:dlg animated:YES completion: Nil];
return 0; // TODO still unfinished
}
cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) {
if (!args->defaultName.length) return SFD_ERR_NEED_DEFAULT_NAME;
// UIDocumentPickerViewController - iOS 8.0
// save the item to a temp file, which is then (usually) later deleted by picker callbacks
Directory_Create(FILEPATH_RAW("Exported"));
save_path.length = 0;
String_Format2(&save_path, "Exported/%s%c", &args->defaultName, args->filters[0]);
args->Callback(&save_path);
NSString* str = ToNSString(&save_path);
NSURL* url = [NSURL fileURLWithPath:str isDirectory:NO];
UIDocumentPickerViewController* dlg;
dlg = [UIDocumentPickerViewController alloc];
dlg = [dlg initWithURL:url inMode:UIDocumentPickerModeExportToService];
dlg.delegate = cc_controller;
[cc_controller presentViewController:dlg animated:YES completion: Nil];
return 0;
}
/*#########################################################################################################################*
*-----------------------------------------------------Window creation-----------------------------------------------------*
*#########################################################################################################################*/
@interface CC3DView : UIView
@end
static void Init3DLayer(void);
void Window_Create2D(int width, int height) {
launcherMode = true;
CGRect bounds = DoCreateWindow();
view_handle = [[UIView alloc] initWithFrame:bounds];
view_handle.multipleTouchEnabled = true;
cc_controller.view = view_handle;
}
void Window_Create3D(int width, int height) {
launcherMode = false;
CGRect bounds = DoCreateWindow();
view_handle = [[CC3DView alloc] initWithFrame:bounds];
view_handle.multipleTouchEnabled = true;
cc_controller.view = view_handle;
Init3DLayer();
}
void Window_Destroy(void) { }
/*########################################################################################################################*
*--------------------------------------------------------GLContext--------------------------------------------------------*
*#########################################################################################################################*/
#if (CC_GFX_BACKEND & CC_GFX_BACKEND_GL_MASK)
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
static EAGLContext* ctx_handle;
static GLuint framebuffer;
static GLuint color_renderbuffer, depth_renderbuffer;
static int fb_width, fb_height;
static void UpdateColorbuffer(void) {
CAEAGLLayer* layer = (CAEAGLLayer*)view_handle.layer;
glBindRenderbuffer(GL_RENDERBUFFER, color_renderbuffer);
if (![ctx_handle renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer])
Logger_Abort("Failed to link renderbuffer to window");
}
static void UpdateDepthbuffer(void) {
int backingW = 0, backingH = 0;
// In case layer dimensions are different
glBindRenderbuffer(GL_RENDERBUFFER, color_renderbuffer);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingW);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingH);
// Shouldn't happen but just in case
if (backingW <= 0) backingW = Window_Main.Width;
if (backingH <= 0) backingH = Window_Main.Height;
glBindRenderbuffer(GL_RENDERBUFFER, depth_renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, backingW, backingH);
}
static void CreateFramebuffer(void) {
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glGenRenderbuffers(1, &color_renderbuffer);
UpdateColorbuffer();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_renderbuffer);
glGenRenderbuffers(1, &depth_renderbuffer);
UpdateDepthbuffer();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_renderbuffer);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
Logger_Abort2(status, "Failed to create renderbuffer");
fb_width = Window_Main.Width;
fb_height = Window_Main.Height;
}
void GLContext_Create(void) {
ctx_handle = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:ctx_handle];
// unlike other platforms, have to manually setup render framebuffer
CreateFramebuffer();
}
void GLContext_Update(void) {
// trying to update renderbuffer here results in garbage output,
// so do instead when layoutSubviews method is called
}
static void GLContext_OnLayout(void) {
// only resize buffers when absolutely have to
if (fb_width == Window_Main.Width && fb_height == Window_Main.Height) return;
fb_width = Window_Main.Width;
fb_height = Window_Main.Height;
UpdateColorbuffer();
UpdateDepthbuffer();
}
void GLContext_Free(void) {
glDeleteRenderbuffers(1, &color_renderbuffer); color_renderbuffer = 0;
glDeleteRenderbuffers(1, &depth_renderbuffer); depth_renderbuffer = 0;
glDeleteFramebuffers(1, &framebuffer); framebuffer = 0;
[EAGLContext setCurrentContext:Nil];
}
cc_bool GLContext_TryRestore(void) { return false; }
void* GLContext_GetAddress(const char* function) { return NULL; }
cc_bool GLContext_SwapBuffers(void) {
static GLenum discards[] = { GL_DEPTH_ATTACHMENT };
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards);
glBindRenderbuffer(GL_RENDERBUFFER, color_renderbuffer);
[ctx_handle presentRenderbuffer:GL_RENDERBUFFER];
return true;
}
void GLContext_SetVSync(cc_bool vsync) { }
void GLContext_GetApiInfo(cc_string* info) { }
@implementation CC3DView
+ (Class)layerClass {
return [CAEAGLLayer class];
}
- (void)layoutSubviews {
[super layoutSubviews];
GLContext_OnLayout();
}
@end
static void Init3DLayer(void) {
// CAEAGLLayer - iOS 2.0
CAEAGLLayer* layer = (CAEAGLLayer*)view_handle.layer;
layer.opaque = YES;
layer.drawableProperties =
@{
kEAGLDrawablePropertyRetainedBacking : [NSNumber numberWithBool:NO],
kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8
};
}
#endif
/*########################################################################################################################*
*-------------------------------------------------------Framebuffer-------------------------------------------------------*
*#########################################################################################################################*/
void Window_AllocFramebuffer(struct Bitmap* bmp, int width, int height) {
bmp->width = width;
bmp->height = height;
bmp->scan0 = (BitmapCol*)Mem_Alloc(width * height, BITMAPCOLOR_SIZE, "window pixels");
win_ctx = CGBitmapContextCreate(bmp->scan0, width, height, 8, width * 4,
CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrder32Host | kCGImageAlphaNoneSkipFirst);
}
void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) {
CGImageRef image = CGBitmapContextCreateImage(win_ctx);
view_handle.layer.contents = CFBridgingRelease(image);
}
void Window_FreeFramebuffer(struct Bitmap* bmp) {
Mem_Free(bmp->scan0);
CGContextRelease(win_ctx);
}
#endif

View File

@ -3,7 +3,6 @@
#include "Core.h"
#if defined CC_BUILD_IOS
#include "_WindowBase.h"
#include "Bitmap.h"
#include "Input.h"
#include "Platform.h"
@ -13,6 +12,9 @@
#include "Launcher.h"
#include "Funcs.h"
#include "Gui.h"
#include "Window.h"
#include "Event.h"
#include "Logger.h"
#include <mach-o/dyld.h>
#include <sys/stat.h>
#include <UIKit/UIKit.h>
@ -29,229 +31,19 @@
#define TEXT_ATTRIBUTE_COLOR UITextAttributeTextColor
#endif
// shared state with LBackend_ios.m
UITextField* kb_widget;
CGContextRef win_ctx;
UIView* view_handle;
// shared state with Window_ios.m
extern UIViewController* cc_controller;
UIColor* ToUIColor(BitmapCol color, float A);
NSString* ToNSString(const cc_string* text);
void LInput_SetKeyboardType(UITextField* fld, int flags);
void LInput_SetPlaceholder(UITextField* fld, const char* placeholder);
UIInterfaceOrientationMask SupportedOrientations(void);
void LogUnhandledNSErrors(NSException* ex);
@interface CCWindow : UIWindow
@end
@interface CCViewController : UIViewController<UIDocumentPickerDelegate, UIAlertViewDelegate>
@end
@interface CCAppDelegate : UIResponder<UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
static CCViewController* cc_controller;
static UIWindow* win_handle;
static cc_bool launcherMode;
static void AddTouch(UITouch* t) {
CGPoint loc = [t locationInView:view_handle];
int x = loc.x, y = loc.y; long ui_id = (long)t;
Platform_Log3("POINTER %x - DOWN %i,%i", &ui_id, &x, &y);
Input_AddTouch((long)t, loc.x, loc.y);
}
static void UpdateTouch(UITouch* t) {
CGPoint loc = [t locationInView:view_handle];
int x = loc.x, y = loc.y; long ui_id = (long)t;
Platform_Log3("POINTER %x - MOVE %i,%i", &ui_id, &x, &y);
Input_UpdateTouch((long)t, loc.x, loc.y);
}
static void RemoveTouch(UITouch* t) {
CGPoint loc = [t locationInView:view_handle];
int x = loc.x, y = loc.y; long ui_id = (long)t;
Platform_Log3("POINTER %x - UP %i,%i", &ui_id, &x, &y);
Input_RemoveTouch((long)t, loc.x, loc.y);
}
static cc_bool landscape_locked;
static UIInterfaceOrientationMask SupportedOrientations(void) {
if (landscape_locked)
return UIInterfaceOrientationMaskLandscape;
return UIInterfaceOrientationMaskAll;
}
static cc_bool fullscreen = true;
static void UpdateStatusBar(void) {
if ([cc_controller respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
// setNeedsStatusBarAppearanceUpdate - iOS 7.0
[cc_controller setNeedsStatusBarAppearanceUpdate];
} else {
[[UIApplication sharedApplication] setStatusBarHidden:fullscreen withAnimation:UIStatusBarAnimationNone];
}
}
static CGRect GetViewFrame(void) {
UIScreen* screen = UIScreen.mainScreen;
return fullscreen ? screen.bounds : screen.applicationFrame;
}
@implementation CCWindow
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event {
// touchesBegan:withEvent - iOS 2.0
for (UITouch* t in touches) AddTouch(t);
// clicking on the background should dismiss onscren keyboard
if (launcherMode) { [view_handle endEditing:NO]; }
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent *)event {
// touchesMoved:withEvent - iOS 2.0
for (UITouch* t in touches) UpdateTouch(t);
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent *)event {
// touchesEnded:withEvent - iOS 2.0
for (UITouch* t in touches) RemoveTouch(t);
}
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent *)event {
// touchesCancelled:withEvent - iOS 2.0
for (UITouch* t in touches) RemoveTouch(t);
}
- (BOOL)isOpaque { return YES; }
@end
@implementation CCViewController
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
// supportedInterfaceOrientations - iOS 6.0
return SupportedOrientations();
}
- (BOOL)shouldAutorotate {
// shouldAutorotate - iOS 6.0
return YES;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)ori {
// shouldAutorotateToInterfaceOrientation - iOS 2.0
if (landscape_locked && !(ori == UIInterfaceOrientationLandscapeLeft || ori == UIInterfaceOrientationLandscapeRight))
return NO;
return YES;
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator {
// viewWillTransitionToSize:withTransitionCoordinator - iOS 8.0
Window_Main.Width = size.width;
Window_Main.Height = size.height;
Event_RaiseVoid(&WindowEvents.Resized);
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
// ==== UIDocumentPickerDelegate ====
static FileDialogCallback open_dlg_callback;
static char save_buffer[FILENAME_SIZE];
static cc_string save_path = String_FromArray(save_buffer);
static void DeleteExportTempFile(void) {
if (!save_path.length) return;
char path[NATIVE_STR_LEN];
String_EncodeUtf8(path, &save_path);
unlink(path);
save_path.length = 0;
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
// documentPicker:didPickDocumentAtURL - iOS 8.0
NSString* str = url.path;
const char* utf8 = str.UTF8String;
char tmpBuffer[NATIVE_STR_LEN];
cc_string tmp = String_FromArray(tmpBuffer);
String_AppendUtf8(&tmp, utf8, String_Length(utf8));
DeleteExportTempFile();
if (!open_dlg_callback) return;
open_dlg_callback(&tmp);
open_dlg_callback = NULL;
}
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
// documentPickerWasCancelled - iOS 8.0
DeleteExportTempFile();
}
static cc_bool kb_active;
- (void)keyboardDidShow:(NSNotification*)notification {
NSDictionary* info = notification.userInfo;
if (kb_active) return;
// TODO this is wrong
// TODO this doesn't actually trigger view resize???
kb_active = true;
double interval = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
NSInteger curve = [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect winFrame = view_handle.frame;
cc_bool can_shift = true;
// would the active input widget be pushed offscreen?
if (kb_widget) {
can_shift = kb_widget.frame.origin.y > kbFrame.size.height;
}
if (can_shift) winFrame.origin.y = -kbFrame.size.height;
kb_widget = nil;
Platform_LogConst("APPEAR");
[UIView animateWithDuration:interval delay: 0.0 options:curve animations:^{
view_handle.frame = winFrame;
} completion:nil];
}
- (void)keyboardDidHide:(NSNotification*)notification {
NSDictionary* info = notification.userInfo;
if (!kb_active) return;
kb_active = false;
kb_widget = nil;
double interval = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
NSInteger curve = [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
CGRect winFrame = view_handle.frame;
winFrame.origin.y = 0;
Platform_LogConst("VANISH");
[UIView animateWithDuration:interval delay: 0.0 options:curve animations:^{
view_handle.frame = winFrame;
} completion:nil];
}
- (BOOL)prefersStatusBarHidden {
// prefersStatusBarHidden - iOS 7.0
return fullscreen;
}
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
// preferredScreenEdgesDeferringSystemGestures - iOS 11.0
// recent iOS versions have a 'bottom home bar', which when swiped up,
// switches out of ClassiCube and to the app list menu
// overriding this forces the user to swipe up twice, which should
// significantly the chance of accidentally triggering this gesture
return UIRectEdgeBottom;
}
// == UIAlertViewDelegate ==
static int alert_completed;
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
alert_completed = true;
}
@end
@implementation CCAppDelegate
- (void)runMainLoop {
@ -321,7 +113,7 @@ static void LogUnhandled(NSString* str) {
}
// TODO: Should really be handled elsewhere, in Logger or ErrorHandler
static void LogUnhandledNSErrors(NSException* ex) {
void LogUnhandledNSErrors(NSException* ex) {
// last chance to log exception details before process dies
LogUnhandled(@"About to die from unhandled NSException..");
LogUnhandled([ex name]);
@ -336,10 +128,6 @@ int main(int argc, char * argv[]) {
}
}
// iOS textfields manage ctrl+c/v
void Clipboard_GetText(cc_string* value) { }
void Clipboard_SetText(const cc_string* value) { }
/*########################################################################################################################*
*------------------------------------------------------Common helpers--------------------------------------------------------*
@ -371,434 +159,6 @@ void Platform_Log(const char* msg, int len) {
}
/*########################################################################################################################*
*---------------------------------------------------------Window----------------------------------------------------------*
*#########################################################################################################################*/
// no cursor on iOS
void Cursor_GetRawPos(int* x, int* y) { *x = 0; *y = 0; }
void Cursor_SetPosition(int x, int y) { }
void Cursor_DoSetVisible(cc_bool visible) { }
void Window_SetTitle(const cc_string* title) {
// TODO: Implement this somehow
}
void Window_PreInit(void) {
DisplayInfo.CursorVisible = true;
}
void Window_Init(void) {
//Window_Main.SoftKeyboard = SOFT_KEYBOARD_RESIZE;
// keyboard now shifts up
Window_Main.SoftKeyboard = SOFT_KEYBOARD_SHIFT;
Input_SetTouchMode(true);
Input.Sources = INPUT_SOURCE_NORMAL;
Gui_SetTouchUI(true);
DisplayInfo.Depth = 32;
DisplayInfo.ScaleX = 1; // TODO dpi scale
DisplayInfo.ScaleY = 1; // TODO dpi scale
NSSetUncaughtExceptionHandler(LogUnhandledNSErrors);
}
void Window_Free(void) { }
static UIColor* CalcBackgroundColor(void) {
// default to purple if no themed background color yet
if (!Launcher_Theme.BackgroundColor)
return UIColor.purpleColor;
return ToUIColor(Launcher_Theme.BackgroundColor, 1.0f);
}
static CGRect DoCreateWindow(void) {
// UIKeyboardWillShowNotification - iOS 2.0
cc_controller = [CCViewController alloc];
UpdateStatusBar();
CGRect bounds = GetViewFrame();
win_handle = [[CCWindow alloc] initWithFrame:bounds];
win_handle.rootViewController = cc_controller;
win_handle.backgroundColor = CalcBackgroundColor();
Window_Main.Exists = true;
Window_Main.UIScaleX = DEFAULT_UI_SCALE_X;
Window_Main.UIScaleY = DEFAULT_UI_SCALE_Y;
Window_Main.Width = bounds.size.width;
Window_Main.Height = bounds.size.height;
Window_Main.SoftKeyboardInstant = true;
NSNotificationCenter* notifications = NSNotificationCenter.defaultCenter;
[notifications addObserver:cc_controller selector:@selector(keyboardDidShow:) name:UIKeyboardWillShowNotification object:nil];
[notifications addObserver:cc_controller selector:@selector(keyboardDidHide:) name:UIKeyboardWillHideNotification object:nil];
return bounds;
}
void Window_SetSize(int width, int height) { }
void Window_Show(void) {
[win_handle makeKeyAndVisible];
}
void Window_RequestClose(void) {
Event_RaiseVoid(&WindowEvents.Closing);
}
void Window_ProcessEvents(float delta) {
SInt32 res;
// manually tick event queue
do {
res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE);
} while (res == kCFRunLoopRunHandledSource);
}
void Gamepads_Init(void) {
}
void Gamepads_Process(float delta) { }
void ShowDialogCore(const char* title, const char* msg) {
// UIAlertController - iOS 8.0
// UIAlertAction - iOS 8.0
// UIAlertView - iOS 2.0
Platform_LogConst(title);
Platform_LogConst(msg);
NSString* _title = [NSString stringWithCString:title encoding:NSASCIIStringEncoding];
NSString* _msg = [NSString stringWithCString:msg encoding:NSASCIIStringEncoding];
alert_completed = false;
#ifdef TARGET_OS_TV
UIAlertController* alert = [UIAlertController alertControllerWithTitle:_title message:_msg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* okBtn = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* act) { alert_completed = true; }];
[alert addAction:okBtn];
[cc_controller presentViewController:alert animated:YES completion: Nil];
#else
UIAlertView* alert = [UIAlertView alloc];
alert = [alert initWithTitle:_title message:_msg delegate:cc_controller cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
#endif
// TODO clicking outside message box crashes launcher
// loop until alert is closed TODO avoid sleeping
while (!alert_completed) {
Window_ProcessEvents(0.0);
Thread_Sleep(16);
}
}
@interface CCKBController : NSObject<UITextFieldDelegate>
@end
@implementation CCKBController
- (void)handleTextChanged:(id)sender {
UITextField* src = (UITextField*)sender;
const char* str = src.text.UTF8String;
char tmpBuffer[NATIVE_STR_LEN];
cc_string tmp = String_FromArray(tmpBuffer);
String_AppendUtf8(&tmp, str, String_Length(str));
Event_RaiseString(&InputEvents.TextChanged, &tmp);
}
// === UITextFieldDelegate ===
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
// textFieldShouldReturn - iOS 2.0
Input_SetPressed(CCKEY_ENTER);
Input_SetReleased(CCKEY_ENTER);
return YES;
}
@end
static UITextField* text_input;
static CCKBController* kb_controller;
void OnscreenKeyboard_Open(struct OpenKeyboardArgs* args) {
if (!kb_controller) {
kb_controller = [[CCKBController alloc] init];
CFBridgingRetain(kb_controller); // prevent GC TODO even needed?
}
DisplayInfo.ShowingSoftKeyboard = true;
text_input = [[UITextField alloc] initWithFrame:CGRectZero];
text_input.hidden = YES;
text_input.delegate = kb_controller;
[text_input addTarget:kb_controller action:@selector(handleTextChanged:) forControlEvents:UIControlEventEditingChanged];
LInput_SetKeyboardType(text_input, args->type);
LInput_SetPlaceholder(text_input, args->placeholder);
[view_handle addSubview:text_input];
[text_input becomeFirstResponder];
}
void OnscreenKeyboard_SetText(const cc_string* text) {
NSString* str = ToNSString(text);
NSString* cur = text_input.text;
// otherwise on iOS 5, this causes an infinite loop
if (cur && [str isEqualToString:cur]) return;
text_input.text = str;
}
void OnscreenKeyboard_Close(void) {
DisplayInfo.ShowingSoftKeyboard = false;
[text_input resignFirstResponder];
}
int Window_GetWindowState(void) {
return fullscreen ? WINDOW_STATE_FULLSCREEN : WINDOW_STATE_NORMAL;
}
static void ToggleFullscreen(cc_bool isFullscreen) {
fullscreen = isFullscreen;
UpdateStatusBar();
view_handle.frame = GetViewFrame();
}
cc_result Window_EnterFullscreen(void) {
ToggleFullscreen(true); return 0;
}
cc_result Window_ExitFullscreen(void) {
ToggleFullscreen(false); return 0;
}
int Window_IsObscured(void) { return 0; }
void Window_EnableRawMouse(void) { DefaultEnableRawMouse(); }
void Window_UpdateRawMouse(void) { }
void Window_DisableRawMouse(void) { DefaultDisableRawMouse(); }
void Window_LockLandscapeOrientation(cc_bool lock) {
// attemptRotationToDeviceOrientation - iOS 5.0
// TODO doesn't work properly.. setting 'UIInterfaceOrientationUnknown' apparently
// restores orientation, but doesn't actually do that when I tried it
if (lock) {
//NSInteger ori = lock ? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationUnknown;
NSInteger ori = UIInterfaceOrientationLandscapeRight;
UIDevice* device = UIDevice.currentDevice;
NSNumber* value = [NSNumber numberWithInteger:ori];
[device setValue:value forKey:@"orientation"];
}
landscape_locked = lock;
[UIViewController attemptRotationToDeviceOrientation];
}
cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) {
// UIDocumentPickerViewController - iOS 8.0
// see the custom UTITypes declared in Info.plist
NSDictionary* fileExt_map =
@{
@".cw" : @"com.classicube.client.ios-cw",
@".dat" : @"com.classicube.client.ios-dat",
@".lvl" : @"com.classicube.client.ios-lvl",
@".fcm" : @"com.classicube.client.ios-fcm",
@".zip" : @"public.zip-archive"
};
NSMutableArray* types = [NSMutableArray array];
const char* const* filters = args->filters;
for (int i = 0; filters[i]; i++)
{
NSString* fileExt = [NSString stringWithUTF8String:filters[i]];
NSString* utType = [fileExt_map objectForKey:fileExt];
if (utType) [types addObject:utType];
}
UIDocumentPickerViewController* dlg;
dlg = [UIDocumentPickerViewController alloc];
dlg = [dlg initWithDocumentTypes:types inMode:UIDocumentPickerModeOpen];
//dlg = [dlg initWithDocumentTypes:types inMode:UIDocumentPickerModeImport];
open_dlg_callback = args->Callback;
dlg.delegate = cc_controller;
[cc_controller presentViewController:dlg animated:YES completion: Nil];
return 0; // TODO still unfinished
}
cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) {
if (!args->defaultName.length) return SFD_ERR_NEED_DEFAULT_NAME;
// UIDocumentPickerViewController - iOS 8.0
// save the item to a temp file, which is then (usually) later deleted by picker callbacks
Directory_Create(FILEPATH_RAW("Exported"));
save_path.length = 0;
String_Format2(&save_path, "Exported/%s%c", &args->defaultName, args->filters[0]);
args->Callback(&save_path);
NSString* str = ToNSString(&save_path);
NSURL* url = [NSURL fileURLWithPath:str isDirectory:NO];
UIDocumentPickerViewController* dlg;
dlg = [UIDocumentPickerViewController alloc];
dlg = [dlg initWithURL:url inMode:UIDocumentPickerModeExportToService];
dlg.delegate = cc_controller;
[cc_controller presentViewController:dlg animated:YES completion: Nil];
return 0;
}
/*#########################################################################################################################*
*-----------------------------------------------------Window creation-----------------------------------------------------*
*#########################################################################################################################*/
@interface CC3DView : UIView
@end
static void Init3DLayer(void);
void Window_Create2D(int width, int height) {
launcherMode = true;
CGRect bounds = DoCreateWindow();
view_handle = [[UIView alloc] initWithFrame:bounds];
view_handle.multipleTouchEnabled = true;
cc_controller.view = view_handle;
}
void Window_Create3D(int width, int height) {
launcherMode = false;
CGRect bounds = DoCreateWindow();
view_handle = [[CC3DView alloc] initWithFrame:bounds];
view_handle.multipleTouchEnabled = true;
cc_controller.view = view_handle;
Init3DLayer();
}
void Window_Destroy(void) { }
/*########################################################################################################################*
*--------------------------------------------------------GLContext--------------------------------------------------------*
*#########################################################################################################################*/
#if (CC_GFX_BACKEND & CC_GFX_BACKEND_GL_MASK)
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
static EAGLContext* ctx_handle;
static GLuint framebuffer;
static GLuint color_renderbuffer, depth_renderbuffer;
static int fb_width, fb_height;
static void UpdateColorbuffer(void) {
CAEAGLLayer* layer = (CAEAGLLayer*)view_handle.layer;
glBindRenderbuffer(GL_RENDERBUFFER, color_renderbuffer);
if (![ctx_handle renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer])
Logger_Abort("Failed to link renderbuffer to window");
}
static void UpdateDepthbuffer(void) {
int backingW = 0, backingH = 0;
// In case layer dimensions are different
glBindRenderbuffer(GL_RENDERBUFFER, color_renderbuffer);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingW);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingH);
// Shouldn't happen but just in case
if (backingW <= 0) backingW = Window_Main.Width;
if (backingH <= 0) backingH = Window_Main.Height;
glBindRenderbuffer(GL_RENDERBUFFER, depth_renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, backingW, backingH);
}
static void CreateFramebuffer(void) {
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glGenRenderbuffers(1, &color_renderbuffer);
UpdateColorbuffer();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_renderbuffer);
glGenRenderbuffers(1, &depth_renderbuffer);
UpdateDepthbuffer();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_renderbuffer);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
Logger_Abort2(status, "Failed to create renderbuffer");
fb_width = Window_Main.Width;
fb_height = Window_Main.Height;
}
void GLContext_Create(void) {
ctx_handle = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:ctx_handle];
// unlike other platforms, have to manually setup render framebuffer
CreateFramebuffer();
}
void GLContext_Update(void) {
// trying to update renderbuffer here results in garbage output,
// so do instead when layoutSubviews method is called
}
static void GLContext_OnLayout(void) {
// only resize buffers when absolutely have to
if (fb_width == Window_Main.Width && fb_height == Window_Main.Height) return;
fb_width = Window_Main.Width;
fb_height = Window_Main.Height;
UpdateColorbuffer();
UpdateDepthbuffer();
}
void GLContext_Free(void) {
glDeleteRenderbuffers(1, &color_renderbuffer); color_renderbuffer = 0;
glDeleteRenderbuffers(1, &depth_renderbuffer); depth_renderbuffer = 0;
glDeleteFramebuffers(1, &framebuffer); framebuffer = 0;
[EAGLContext setCurrentContext:Nil];
}
cc_bool GLContext_TryRestore(void) { return false; }
void* GLContext_GetAddress(const char* function) { return NULL; }
cc_bool GLContext_SwapBuffers(void) {
static GLenum discards[] = { GL_DEPTH_ATTACHMENT };
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards);
glBindRenderbuffer(GL_RENDERBUFFER, color_renderbuffer);
[ctx_handle presentRenderbuffer:GL_RENDERBUFFER];
return true;
}
void GLContext_SetVSync(cc_bool vsync) { }
void GLContext_GetApiInfo(cc_string* info) { }
@implementation CC3DView
+ (Class)layerClass {
return [CAEAGLLayer class];
}
- (void)layoutSubviews {
[super layoutSubviews];
GLContext_OnLayout();
}
@end
static void Init3DLayer(void) {
// CAEAGLLayer - iOS 2.0
CAEAGLLayer* layer = (CAEAGLLayer*)view_handle.layer;
layer.opaque = YES;
layer.drawableProperties =
@{
kEAGLDrawablePropertyRetainedBacking : [NSNumber numberWithBool:NO],
kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8
};
}
#endif
/*########################################################################################################################*
*--------------------------------------------------------Updater----------------------------------------------------------*
*#########################################################################################################################*/
@ -1165,23 +525,4 @@ void interop_SysTextDraw(struct DrawTextArgs* args, struct Context2D* ctx, int x
}*/
#endif
void Window_AllocFramebuffer(struct Bitmap* bmp, int width, int height) {
bmp->width = width;
bmp->height = height;
bmp->scan0 = (BitmapCol*)Mem_Alloc(width * height, BITMAPCOLOR_SIZE, "window pixels");
win_ctx = CGBitmapContextCreate(bmp->scan0, width, height, 8, width * 4,
CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrder32Host | kCGImageAlphaNoneSkipFirst);
}
void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) {
CGImageRef image = CGBitmapContextCreateImage(win_ctx);
view_handle.layer.contents = CFBridgingRelease(image);
}
void Window_FreeFramebuffer(struct Bitmap* bmp) {
Mem_Free(bmp->scan0);
CGContextRelease(win_ctx);
}
#endif