mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-21 21:16:28 +02:00
fix(ios): support mod index networking and text input dismissal
This commit is contained in:
@@ -30,6 +30,45 @@ public final class GRPickerBridge: NSObject {
|
|||||||
// (<sandbox>/Library/Application Support/<identity>).
|
// (<sandbox>/Library/Application Support/<identity>).
|
||||||
private static let loveIdentity = "pokemon-love2d"
|
private static let loveIdentity = "pokemon-love2d"
|
||||||
|
|
||||||
|
@objc(httpDownloadWithUrl:destination:userAgent:accept:)
|
||||||
|
public static func httpDownload(url: UnsafePointer<CChar>?,
|
||||||
|
destination: UnsafePointer<CChar>?,
|
||||||
|
userAgent: UnsafePointer<CChar>?,
|
||||||
|
accept: UnsafePointer<CChar>?) -> Bool {
|
||||||
|
guard let url, let destination,
|
||||||
|
let requestURL = URL(string: String(cString: url)) else { return false }
|
||||||
|
var request = URLRequest(url: requestURL)
|
||||||
|
request.timeoutInterval = 300
|
||||||
|
if let userAgent, userAgent.pointee != 0 {
|
||||||
|
request.setValue(String(cString: userAgent), forHTTPHeaderField: "User-Agent")
|
||||||
|
}
|
||||||
|
if let accept, accept.pointee != 0 {
|
||||||
|
request.setValue(String(cString: accept), forHTTPHeaderField: "Accept")
|
||||||
|
}
|
||||||
|
let target = URL(fileURLWithPath: String(cString: destination))
|
||||||
|
let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
var succeeded = false
|
||||||
|
let task = URLSession.shared.downloadTask(with: request) { temporary, response, error in
|
||||||
|
defer { semaphore.signal() }
|
||||||
|
guard error == nil, let temporary,
|
||||||
|
let http = response as? HTTPURLResponse,
|
||||||
|
(200..<300).contains(http.statusCode) else { return }
|
||||||
|
try? FileManager.default.removeItem(at: target)
|
||||||
|
do {
|
||||||
|
try FileManager.default.moveItem(at: temporary, to: target)
|
||||||
|
succeeded = true
|
||||||
|
} catch {
|
||||||
|
succeeded = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
task.resume()
|
||||||
|
guard semaphore.wait(timeout: .now() + 305) == .success else {
|
||||||
|
task.cancel()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return succeeded
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Entry points called from liblove (C strings on purpose)
|
// MARK: - Entry points called from liblove (C strings on purpose)
|
||||||
|
|
||||||
@objc(presentPickerWithKind:saveDir:)
|
@objc(presentPickerWithKind:saveDir:)
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ WRAP_REGISTRATION = """#ifdef LOVE_IOS
|
|||||||
{ "pickFile", w_pickFile },
|
{ "pickFile", w_pickFile },
|
||||||
{ "createFile", w_createFile },
|
{ "createFile", w_createFile },
|
||||||
{ "syncHealthSteps", w_syncHealthSteps },
|
{ "syncHealthSteps", w_syncHealthSteps },
|
||||||
|
{ "httpDownload", w_httpDownload },
|
||||||
#endif
|
#endif
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -144,6 +145,32 @@ int w_syncHealthSteps(lua_State *L)
|
|||||||
|
|
||||||
WRAP_SYNC_REGISTRATION = """#ifdef LOVE_IOS
|
WRAP_SYNC_REGISTRATION = """#ifdef LOVE_IOS
|
||||||
{ "syncHealthSteps", w_syncHealthSteps },
|
{ "syncHealthSteps", w_syncHealthSteps },
|
||||||
|
{ "httpDownload", w_httpDownload },
|
||||||
|
#endif
|
||||||
|
"""
|
||||||
|
|
||||||
|
BRIDGE_EXTRA_FUNCS = """
|
||||||
|
#ifdef LOVE_IOS
|
||||||
|
int w_httpDownload(lua_State *L)
|
||||||
|
{
|
||||||
|
const char *url = luaL_checkstring(L, 1);
|
||||||
|
const char *destination = luaL_checkstring(L, 2);
|
||||||
|
const char *userAgent = luaL_optstring(L, 3, "gen1recomp");
|
||||||
|
const char *accept = luaL_optstring(L, 4, "");
|
||||||
|
Class cls = objc_getClass("GRPickerBridge");
|
||||||
|
if (cls == nullptr)
|
||||||
|
{
|
||||||
|
lua_pushboolean(L, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
typedef signed char (*GRDownload)(Class, SEL, const char *, const char *,
|
||||||
|
const char *, const char *);
|
||||||
|
signed char ok = ((GRDownload)objc_msgSend)(
|
||||||
|
cls, sel_registerName("httpDownloadWithUrl:destination:userAgent:accept:"),
|
||||||
|
url, destination, userAgent, accept);
|
||||||
|
lua_pushboolean(L, ok != 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -216,7 +243,8 @@ def patch_wrap_system():
|
|||||||
if anchor not in text:
|
if anchor not in text:
|
||||||
fail(f"anchor not found in {WRAP_SYSTEM}")
|
fail(f"anchor not found in {WRAP_SYSTEM}")
|
||||||
has_native_picker = re.search(r"\bint w_pickFile\s*\(", text) is not None
|
has_native_picker = re.search(r"\bint w_pickFile\s*\(", text) is not None
|
||||||
text = text.replace(anchor, (WRAP_SYNC_FUNCS if has_native_picker else WRAP_FUNCS) + anchor, 1)
|
bridge_funcs = WRAP_SYNC_FUNCS if has_native_picker else WRAP_FUNCS
|
||||||
|
text = text.replace(anchor, bridge_funcs + BRIDGE_EXTRA_FUNCS + anchor, 1)
|
||||||
reg_anchor = '\t{ "vibrate", w_vibrate },\n'
|
reg_anchor = '\t{ "vibrate", w_vibrate },\n'
|
||||||
if reg_anchor not in text:
|
if reg_anchor not in text:
|
||||||
fail(f"registration anchor not found in {WRAP_SYSTEM}")
|
fail(f"registration anchor not found in {WRAP_SYSTEM}")
|
||||||
@@ -224,7 +252,7 @@ def patch_wrap_system():
|
|||||||
text = text.replace(reg_anchor, reg_anchor + registration, 1)
|
text = text.replace(reg_anchor, reg_anchor + registration, 1)
|
||||||
WRAP_SYSTEM.write_text(text)
|
WRAP_SYSTEM.write_text(text)
|
||||||
print("patch_love_src: wrap_System.cpp patched "
|
print("patch_love_src: wrap_System.cpp patched "
|
||||||
"(pickFile/createFile/syncHealthSteps)")
|
"(pickFile/createFile/syncHealthSteps/httpDownload)")
|
||||||
|
|
||||||
|
|
||||||
def patch_pbxproj():
|
def patch_pbxproj():
|
||||||
|
|||||||
@@ -148,15 +148,14 @@ function HostShell.haveCurl()
|
|||||||
return readOk and out ~= nil and out:find("curl", 1, true) ~= nil
|
return readOk and out ~= nil and out:find("curl", 1, true) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- The bridge only exists in our Android liblove. An older APK reports nil
|
-- An older mobile build reports nil here and falls back to the "no transport"
|
||||||
-- here and falls back to the "no transport" error the callers already show;
|
-- error the callers already show.
|
||||||
-- the iOS build compiles the same wrapper but always returns false, so gate
|
|
||||||
-- on the OS as well and keep its error message honest.
|
|
||||||
local function haveBridge()
|
local function haveBridge()
|
||||||
if not (love and love.system and type(love.system.httpDownload) == "function") then
|
if not (love and love.system and type(love.system.httpDownload) == "function") then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return love.system.getOS and love.system.getOS() == "Android"
|
local osName = love.system.getOS and love.system.getOS()
|
||||||
|
return osName == "Android" or osName == "iOS"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Is any transport available at all? Callers gate on this, never on curl.
|
-- Is any transport available at all? Callers gate on this, never on curl.
|
||||||
|
|||||||
@@ -1214,8 +1214,7 @@ local function buildFindPanel(imp, parent, m)
|
|||||||
imp.findQuery or "", Strings("Search mods"),
|
imp.findQuery or "", Strings("Search mods"),
|
||||||
imp._findSearchFocus == true,
|
imp._findSearchFocus == true,
|
||||||
function()
|
function()
|
||||||
imp._findSearchFocus = true
|
imp:_toggleFindSearchFocus()
|
||||||
imp:_armTextInput()
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local cats = (imp.findIndex and imp.findIndex.categories) or {}
|
local cats = (imp.findIndex and imp.findIndex.categories) or {}
|
||||||
|
|||||||
@@ -1733,6 +1733,15 @@ function RomImporter:_switchTab(id)
|
|||||||
self:_disarmTextInput()
|
self:_disarmTextInput()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function RomImporter:_toggleFindSearchFocus()
|
||||||
|
self._findSearchFocus = not self._findSearchFocus
|
||||||
|
if self._findSearchFocus then
|
||||||
|
self:_armTextInput()
|
||||||
|
else
|
||||||
|
self:_disarmTextInput()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- ------- settings gear (options.lua + enabled mods' option schemas)
|
-- ------- settings gear (options.lua + enabled mods' option schemas)
|
||||||
|
|
||||||
function RomImporter:_openSettings()
|
function RomImporter:_openSettings()
|
||||||
|
|||||||
@@ -133,6 +133,13 @@ check(ri._findSearchFocus == false, "a tab change drops the caret")
|
|||||||
eq(lastArm(), false, "and disarms setTextInput")
|
eq(lastArm(), false, "and disarms setTextInput")
|
||||||
ri.tab = "find"
|
ri.tab = "find"
|
||||||
|
|
||||||
|
ri:_toggleFindSearchFocus()
|
||||||
|
check(ri._findSearchFocus == true, "tapping the search field focuses it")
|
||||||
|
eq(lastArm(), true, "refocusing the search field arms setTextInput")
|
||||||
|
ri:_toggleFindSearchFocus()
|
||||||
|
check(ri._findSearchFocus == false, "tapping the focused search field blurs it")
|
||||||
|
eq(lastArm(), false, "blurring the search field disarms setTextInput")
|
||||||
|
|
||||||
-- ---- desktop contract (#529): disarm never lowers off Android -------------
|
-- ---- desktop contract (#529): disarm never lowers off Android -------------
|
||||||
|
|
||||||
ri.android = false
|
ri.android = false
|
||||||
|
|||||||
Reference in New Issue
Block a user