Files
cell/playdate/network_playdate.c
2025-12-10 15:01:20 -06:00

291 lines
11 KiB
C

// network_playdate.c - Cell integration for Playdate Network API
// Wraps pd_api_network.h functions for JavaScript access
#include "cell.h"
#include "common.h"
#include <string.h>
#include <stdlib.h>
// --- Helpers ---
static HTTPConnection* js2http(JSContext *js, JSValueConst val) {
int64_t ptr;
if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
return (HTTPConnection*)(intptr_t)ptr;
}
static TCPConnection* js2tcp(JSContext *js, JSValueConst val) {
int64_t ptr;
if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
return (TCPConnection*)(intptr_t)ptr;
}
// --- Network Status ---
JSC_CCALL(network_getStatus,
if (!pd_network) return JS_ThrowInternalError(js, "network not initialized");
return JS_NewInt32(js, pd_network->getStatus());
)
JSC_CCALL(network_setEnabled,
if (!pd_network) return JS_ThrowInternalError(js, "network not initialized");
// Note: callback not implemented for simplicity
pd_network->setEnabled(JS_ToBool(js, argv[0]), NULL);
return JS_UNDEFINED;
)
// --- HTTP Functions ---
JSC_SCALL(http_newConnection,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
int port = (int)js2number(js, argv[1]);
int usessl = argc > 2 ? JS_ToBool(js, argv[2]) : 0;
HTTPConnection *conn = pd_network->http->newConnection(str, port, usessl);
ret = conn ? JS_NewInt64(js, (int64_t)(intptr_t)conn) : JS_NULL;
)
JSC_CCALL(http_retain,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
HTTPConnection *conn = js2http(js, argv[0]);
HTTPConnection *ret_conn = pd_network->http->retain(conn);
return ret_conn ? JS_NewInt64(js, (int64_t)(intptr_t)ret_conn) : JS_NULL;
)
JSC_CCALL(http_release,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
HTTPConnection *conn = js2http(js, argv[0]);
if (conn) pd_network->http->release(conn);
return JS_UNDEFINED;
)
JSC_CCALL(http_setConnectTimeout,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
pd_network->http->setConnectTimeout(js2http(js, argv[0]), (int)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(http_setKeepAlive,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
pd_network->http->setKeepAlive(js2http(js, argv[0]), JS_ToBool(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(http_setByteRange,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
pd_network->http->setByteRange(js2http(js, argv[0]), (int)js2number(js, argv[1]), (int)js2number(js, argv[2]));
return JS_UNDEFINED;
)
JSC_SCALL(http_get,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
HTTPConnection *conn = js2http(js, argv[0]);
const char *headers = argc > 2 ? JS_ToCString(js, argv[2]) : NULL;
size_t hlen = headers ? strlen(headers) : 0;
PDNetErr err = pd_network->http->get(conn, str, headers, hlen);
if (headers) JS_FreeCString(js, headers);
ret = JS_NewInt32(js, err);
)
JSC_SCALL(http_post,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
HTTPConnection *conn = js2http(js, argv[0]);
const char *headers = argc > 2 ? JS_ToCString(js, argv[2]) : NULL;
size_t hlen = headers ? strlen(headers) : 0;
size_t blen;
const char *body = argc > 3 ? js_get_blob_data(js, &blen, argv[3]) : NULL;
if (body == (void*)-1) body = NULL;
PDNetErr err = pd_network->http->post(conn, str, headers, hlen, body, body ? blen : 0);
if (headers) JS_FreeCString(js, headers);
ret = JS_NewInt32(js, err);
)
JSC_CCALL(http_getError,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
return JS_NewInt32(js, pd_network->http->getError(js2http(js, argv[0])));
)
JSC_CCALL(http_getProgress,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
int read, total;
pd_network->http->getProgress(js2http(js, argv[0]), &read, &total);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "read", JS_NewInt32(js, read));
JS_SetPropertyStr(js, obj, "total", JS_NewInt32(js, total));
return obj;
)
JSC_CCALL(http_getResponseStatus,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
return JS_NewInt32(js, pd_network->http->getResponseStatus(js2http(js, argv[0])));
)
JSC_CCALL(http_getBytesAvailable,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
return JS_NewInt64(js, pd_network->http->getBytesAvailable(js2http(js, argv[0])));
)
JSC_CCALL(http_setReadTimeout,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
pd_network->http->setReadTimeout(js2http(js, argv[0]), (int)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(http_setReadBufferSize,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
pd_network->http->setReadBufferSize(js2http(js, argv[0]), (int)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(http_read,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
HTTPConnection *conn = js2http(js, argv[0]);
unsigned int buflen = (unsigned int)js2number(js, argv[1]);
void *buf = malloc(buflen);
if (!buf) return JS_ThrowInternalError(js, "malloc failed");
int read = pd_network->http->read(conn, buf, buflen);
if (read < 0) {
free(buf);
return JS_NULL;
}
JSValue blob = js_new_blob_stoned_copy(js, buf, read);
free(buf);
return blob;
)
JSC_CCALL(http_close,
if (!pd_network || !pd_network->http) return JS_ThrowInternalError(js, "network not initialized");
pd_network->http->close(js2http(js, argv[0]));
return JS_UNDEFINED;
)
// --- TCP Functions ---
JSC_SCALL(tcp_newConnection,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
int port = (int)js2number(js, argv[1]);
int usessl = argc > 2 ? JS_ToBool(js, argv[2]) : 0;
TCPConnection *conn = pd_network->tcp->newConnection(str, port, usessl);
ret = conn ? JS_NewInt64(js, (int64_t)(intptr_t)conn) : JS_NULL;
)
JSC_CCALL(tcp_retain,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
TCPConnection *conn = js2tcp(js, argv[0]);
TCPConnection *ret_conn = pd_network->tcp->retain(conn);
return ret_conn ? JS_NewInt64(js, (int64_t)(intptr_t)ret_conn) : JS_NULL;
)
JSC_CCALL(tcp_release,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
TCPConnection *conn = js2tcp(js, argv[0]);
if (conn) pd_network->tcp->release(conn);
return JS_UNDEFINED;
)
JSC_CCALL(tcp_getError,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
return JS_NewInt32(js, pd_network->tcp->getError(js2tcp(js, argv[0])));
)
JSC_CCALL(tcp_setConnectTimeout,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
pd_network->tcp->setConnectTimeout(js2tcp(js, argv[0]), (int)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(tcp_close,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
return JS_NewInt32(js, pd_network->tcp->close(js2tcp(js, argv[0])));
)
JSC_CCALL(tcp_setReadTimeout,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
pd_network->tcp->setReadTimeout(js2tcp(js, argv[0]), (int)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(tcp_setReadBufferSize,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
pd_network->tcp->setReadBufferSize(js2tcp(js, argv[0]), (int)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(tcp_getBytesAvailable,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
return JS_NewInt64(js, pd_network->tcp->getBytesAvailable(js2tcp(js, argv[0])));
)
JSC_CCALL(tcp_read,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
TCPConnection *conn = js2tcp(js, argv[0]);
size_t len = (size_t)js2number(js, argv[1]);
void *buf = malloc(len);
if (!buf) return JS_ThrowInternalError(js, "malloc failed");
int read = pd_network->tcp->read(conn, buf, len);
if (read < 0) {
free(buf);
return JS_NewInt32(js, read); // Return error code
}
JSValue blob = js_new_blob_stoned_copy(js, buf, read);
free(buf);
return blob;
)
JSC_CCALL(tcp_write,
if (!pd_network || !pd_network->tcp) return JS_ThrowInternalError(js, "network not initialized");
TCPConnection *conn = js2tcp(js, argv[0]);
size_t len;
const void *data = js_get_blob_data(js, &len, argv[1]);
if (data == (void*)-1) return JS_EXCEPTION;
return JS_NewInt32(js, pd_network->tcp->write(conn, data, len));
)
static const JSCFunctionListEntry js_network_funcs[] = {
MIST_FUNC_DEF(network, getStatus, 0),
MIST_FUNC_DEF(network, setEnabled, 1),
// HTTP
MIST_FUNC_DEF(http, newConnection, 3),
MIST_FUNC_DEF(http, retain, 1),
MIST_FUNC_DEF(http, release, 1),
MIST_FUNC_DEF(http, setConnectTimeout, 2),
MIST_FUNC_DEF(http, setKeepAlive, 2),
MIST_FUNC_DEF(http, setByteRange, 3),
MIST_FUNC_DEF(http, get, 3),
MIST_FUNC_DEF(http, post, 4),
MIST_FUNC_DEF(http, getError, 1),
MIST_FUNC_DEF(http, getProgress, 1),
MIST_FUNC_DEF(http, getResponseStatus, 1),
MIST_FUNC_DEF(http, getBytesAvailable, 1),
MIST_FUNC_DEF(http, setReadTimeout, 2),
MIST_FUNC_DEF(http, setReadBufferSize, 2),
MIST_FUNC_DEF(http, read, 2),
MIST_FUNC_DEF(http, close, 1),
// TCP
MIST_FUNC_DEF(tcp, newConnection, 3),
MIST_FUNC_DEF(tcp, retain, 1),
MIST_FUNC_DEF(tcp, release, 1),
MIST_FUNC_DEF(tcp, getError, 1),
MIST_FUNC_DEF(tcp, setConnectTimeout, 2),
MIST_FUNC_DEF(tcp, close, 1),
MIST_FUNC_DEF(tcp, setReadTimeout, 2),
MIST_FUNC_DEF(tcp, setReadBufferSize, 2),
MIST_FUNC_DEF(tcp, getBytesAvailable, 1),
MIST_FUNC_DEF(tcp, read, 2),
MIST_FUNC_DEF(tcp, write, 2),
};
JSValue js_network_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_network_funcs, countof(js_network_funcs));
// Export constants
JS_SetPropertyStr(js, mod, "NET_OK", JS_NewInt32(js, NET_OK));
JS_SetPropertyStr(js, mod, "NET_NO_DEVICE", JS_NewInt32(js, NET_NO_DEVICE));
JS_SetPropertyStr(js, mod, "NET_BUSY", JS_NewInt32(js, NET_BUSY));
JS_SetPropertyStr(js, mod, "NET_WRITE_ERROR", JS_NewInt32(js, NET_WRITE_ERROR));
JS_SetPropertyStr(js, mod, "NET_READ_ERROR", JS_NewInt32(js, NET_READ_ERROR));
JS_SetPropertyStr(js, mod, "NET_CONNECTION_CLOSED", JS_NewInt32(js, NET_CONNECTION_CLOSED));
JS_SetPropertyStr(js, mod, "WIFI_NOT_CONNECTED", JS_NewInt32(js, kWifiNotConnected));
JS_SetPropertyStr(js, mod, "WIFI_CONNECTED", JS_NewInt32(js, kWifiConnected));
JS_SetPropertyStr(js, mod, "WIFI_NOT_AVAILABLE", JS_NewInt32(js, kWifiNotAvailable));
return mod;
}