diff --git a/desktop/Makefile b/desktop/Makefile
index 5ad7234..23295b3 100644
--- a/desktop/Makefile
+++ b/desktop/Makefile
@@ -3,8 +3,8 @@ include cross.mk
FUDGE = ../lib
CAMLIB_CORE := transport.o operations.o packet.o enums.o data.o enum_dump.o lib.o canon.o liveview.o bind.o ml.o conv.o generic.o canon_adv.o
-OBJ := main.o ui.o jank.o wifi.o backend.o
-FUDGE_CORE := fuji.o tester.o data.o net.o discovery.o exif.o uilua.o fuji_usb.o object.o
+OBJ := main.o ui.o jank.o os.o backend.o
+FUDGE_CORE := fuji.o tester.o data.o net.o discovery.o exif.o uilua.o fuji_usb.o object.o fuji_lua.o lua_runtime.o
ifeq ($(TARGET),l)
CAMLIB_CORE += libusb.o
@@ -16,7 +16,7 @@ OBJ += win.o
endif
OBJ += $(addprefix camlib-,$(CAMLIB_CORE))
-OBJ += $(addprefix camlua-,lua.o runtime.o)
+OBJ += $(addprefix camlua-,lua.o)
OBJ += $(addprefix camluajson-,lua_cjson.o strbuf.o)
OBJ += $(addprefix fudge-,$(FUDGE_CORE))
OBJ := $(call convert_target,$(OBJ))
@@ -28,7 +28,7 @@ CFLAGS := -I$(FUDGE) -I$(CAMLIB_DIR)/src -I$(CAMLIB_DIR)/src/lua -g
# -fsanitize=address -static-libasan
ifeq ($(TARGET),w) # -----------------------
-WIN_LIBS += $(LIBWPD_A) $(LIBLUA_A) $(LIBUI_DLL)
+WIN_LIBS += $(LIBWPD_A) $(LIBLUA_A) $(LIBUI_A)
WIN_LIBS += $(WIN_LINK_ESSENTIALS) -lstdc++ -lgcc -lpthread
fudge.exe: $(OBJ) win.res
@@ -36,10 +36,12 @@ fudge.exe: $(OBJ) win.res
endif # --------------------
ifeq ($(TARGET),l) # ----------------------
-CFLAGS += $(shell pkg-config --libs --cflags lua-5.3 libusb-1.0)
-LDFLAGS += -lm -lui
+CFLAGS += $(shell pkg-config --cflags lua-5.3 libusb-1.0)
+LDFLAGS += -lm -lui $(shell pkg-config --libs lua-5.3)
+LDFLAGS += $(shell pkg-config --libs libusb-1.0)
+#LDFLAGS += -lusb-vcam
fudge.out: $(OBJ)
- $(CC) $(OBJ) $(CFLAGS) $(LDFLAGS) -o fudge.out
+ $(CC) $(OBJ) $(LDFLAGS) -o fudge.out
endif # ----------------------------
# Manual targets to compile in this dir
diff --git a/desktop/assets/libui.manifest b/desktop/assets/libui.manifest
new file mode 100644
index 0000000..d8e83a8
--- /dev/null
+++ b/desktop/assets/libui.manifest
@@ -0,0 +1,32 @@
+
+
+
+Your application description here.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/desktop/backend.c b/desktop/backend.c
index 931cb0e..02c56a9 100644
--- a/desktop/backend.c
+++ b/desktop/backend.c
@@ -1,11 +1,151 @@
#include
+#include
#include
+#include
+#include
+#include
+#include
#include
+#include
+#include
+#include
+#include
+#include "desktop.h"
-void app_get_file_path(char buffer[256], const char *filename) {
- snprintf(buffer, 256, "%s", filename);
+static struct PtpRuntime *ptp = NULL;
+
+// TODO: System to manage a list of cameras connected
+struct PtpRuntime *ptp_get() {
+ return ptp;
+}
+
+struct PtpRuntime *luaptp_get_runtime(lua_State *L) {
+ return ptp_get();
+}
+
+int luaopen_libuilua(lua_State *L);
+int cam_lua_setup(lua_State *L) {
+ luaL_requiref(L, "ui", luaopen_libuilua, 1);
+ return 0;
}
-void app_get_tether_file_path(char buffer[256]) {
- snprintf(buffer, 256, "TETHER.JPG");
+int fudge_usb_connect(struct PtpRuntime *r) {
+ static int attempts = 0;
+ app_log_clear();
+ int rc = fujiusb_try_connect(r);
+ if (rc) {
+ const char *msg = "No Fujifilm device found";
+ if (attempts) {
+ app_print("%s (%d)", msg, attempts);
+ } else {
+ app_print("%s", msg);
+ }
+ attempts++;
+ return rc;
+ }
+ attempts = 0;
+
+ rc = ptp_open_session(r);
+ if (rc != PTP_CHECK_CODE && rc) return rc;
+
+ ptp = r;
+
+ return 0;
+}
+
+void fudge_disconnect_all(void) {
+ struct PtpRuntime *r = ptp_get();
+ if (r == NULL) {
+ // 0 cameras connected
+ return;
+ }
+ printf("Disconnecting\n");
+ ptp_report_error(r, "Intentional", 0);
+ ptp = NULL;
+}
+
+int fudge_main_app_thread(struct PtpRuntime *r) {
+ struct PtpDeviceInfo di;
+ int rc = ptp_get_device_info(r, &di);
+ if (rc) return rc;
+
+ app_print("Connected to %s %s", di.manufacturer, di.model);
+ app_send_cam_name(di.model);
+
+ struct PtpArray *arr;
+ rc = ptp_fuji_get_object_handles(r, &arr);
+ app_print("%d files on card", arr->length);
+
+ while (r->io_kill_switch == 0) {
+ if (ptpusb_get_status(r)) {
+ app_print("USB cable disconnected.");
+ break;
+ }
+ usleep(100000);
+ }
+
+ return 0;
+}
+
+void *fudge_backup_settings(void *arg) {
+ return NULL;
+}
+
+void *fudge_usb_connect_thread(void *arg) {
+ struct PtpRuntime *r = ptp_new(PTP_USB);
+ int rc = fudge_usb_connect(r);
+ if (rc) goto exit;
+
+ app_update_connected_status(1);
+ rc = fudge_main_app_thread(r);
+ app_update_connected_status(0);
+
+ ptp = NULL;
+
+ exit:;
+ ptp_close(r);
+ pthread_exit(NULL);
+ return NULL;
+}
+
+int fudge_run_lua(struct PtpRuntime *r, const char *text) {
+ cam_run_lua_script_async(text);
+}
+
+static char *read_file(const char *filename) {
+ FILE* file = fopen(filename, "rb");
+ if (!file) return NULL;
+
+ fseek(file, 0, SEEK_END);
+ long file_size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ char* buffer = (char*)malloc(file_size + 1);
+ if (!buffer) return NULL;
+
+ fread(buffer, 1, file_size, file);
+ buffer[file_size] = '\0';
+
+ fclose(file);
+ return buffer;
+}
+
+int fuji_connect_run_script(const char *filename) {
+ const char *text = read_file(filename);
+ if (text == NULL) {
+ printf("%s not found\n", filename);
+ return -1;
+ }
+
+ struct PtpRuntime *r = ptp_new(PTP_USB);
+ int rc = fudge_usb_connect(r);
+ if (rc) return rc;
+
+ rc = cam_run_lua_script_async(text);
+ if (rc) {
+ printf("%s\n", cam_lua_get_error());
+ printf("Lua err %d\n", rc);
+ }
+ return rc;
+ // leak r
}
diff --git a/desktop/ci.c b/desktop/ci.c
index 4e3fbf9..30d6522 100644
--- a/desktop/ci.c
+++ b/desktop/ci.c
@@ -32,9 +32,16 @@ static int discovery_test() {
return 0;
}
+void nothing(int x) {}
+
pid_t child_pid = -1;
int fudge_test_all_cameras_(const char *name) {
+ signal(SIGUSR1, nothing);
+ char thispid[16];
+ sprintf(thispid, "%d", getpid());
+ const char *ip_addr = "0.0.0.0";
+
child_pid = fork();
if (child_pid == -1) {
perror("fork failed");
@@ -42,24 +49,22 @@ int fudge_test_all_cameras_(const char *name) {
}
if (child_pid == 0) {
- int rc = execlp("/usr/bin/vcam", "vcam", name, "--ip", "0.0.0.0", NULL);
+ int rc = execlp("/usr/bin/vcam", "vcam", name, "--ip", ip_addr, "--sig", thispid, NULL);
printf("Return value: %d\n", rc);
exit(0);
}
- printf("Crappy wait for vcam\n");
- usleep(10000);
- printf("Done waiting on vcam\n");
+ printf("Waiting for vcam...\n");
+ pause();
int rc = 0;
- char *ip_addr = "0.0.0.0";
struct PtpRuntime *r = ptp_new(PTP_IP_USB);
fuji_reset_ptp(r);
fuji_get(r)->transport = FUJI_FEATURE_WIRELESS_COMM;
if (ptpip_connect(r, ip_addr, FUJI_CMD_IP_PORT, 0)) {
printf("Error connecting to %s:%d\n", ip_addr, FUJI_CMD_IP_PORT);
- return 0;
+ return -1;
}
rc = fuji_test_setup(r);
diff --git a/desktop/cross.mk b/desktop/cross.mk
index 28b31d9..2af9e60 100644
--- a/desktop/cross.mk
+++ b/desktop/cross.mk
@@ -17,10 +17,10 @@ MINGW := x86_64-w64-mingw32
CC := $(MINGW)-gcc
CPP := $(MINGW)-c++
-LIBWPD_A := /usr/x86_64-w64-mingw32/lib/libwpd.a
-LIBLUA_A := /usr/x86_64-w64-mingw32/lib/liblua.a
-LIBUI_DLL := /usr/x86_64-w64-mingw32/lib/libui_win64.dll
-LIBUI_A := /usr/x86_64-w64-mingw32/lib/libui.a
+LIBWPD_A := /usr/$(ARCH)-w64-mingw32/lib/libwpd.a
+LIBLUA_A := /usr/$(ARCH)-w64-mingw32/lib/liblua.a
+LIBUI_DLL := /usr/$(ARCH)-w64-mingw32/lib/libui_win64.dll
+LIBUI_A := /usr/$(ARCH)-w64-mingw32/lib/libui.a
WIN_LINK_ESSENTIALS += -luser32 -lkernel32 -lgdi32 -lcomctl32 -luxtheme -lmsimg32 -lcomdlg32 -ld2d1 -ldwrite -lole32 -loleaut32 -loleacc -lssp -lurlmon -luuid -lws2_32
diff --git a/desktop/desktop.h b/desktop/desktop.h
index 103dd38..921f14c 100644
--- a/desktop/desktop.h
+++ b/desktop/desktop.h
@@ -8,9 +8,20 @@ void network_init(void);
int fudge_main_ui(void);
-int fuji_test_discovery(struct PtpRuntime *r);
+void app_log_clear(void);
+void app_update_connected_status(int connected);
+void fudge_disconnect_all(void);
+
+void *fudge_usb_connect_thread(void *arg);
+
+void *fudge_backup_settings(void *arg);
+int fuji_connect_run_script(const char *filename);
+
+// Tests
+int fuji_test_discovery(struct PtpRuntime *r);
int fuji_test_filesystem(struct PtpRuntime *r);
int fuji_test_setup(struct PtpRuntime *r);
+
#endif
diff --git a/desktop/file_table.c b/desktop/file_table.c
new file mode 100644
index 0000000..6079fd6
--- /dev/null
+++ b/desktop/file_table.c
@@ -0,0 +1,79 @@
+#include
+#include
+#include
+#include
+#include
+
+struct MyTableModelHandler {
+ uiTableModelHandler mh;
+ struct PtpRuntime *r;
+ struct PtpObjectCache *oc;
+};
+
+static inline struct MyTableModelHandler *my_handler(uiTableModelHandler *mh) {
+ return (struct MyTableModelHandler *)mh;
+}
+
+static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) {
+ return 3;
+}
+
+static uiTableValueType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column)
+{
+ switch (column) {
+ case 0:
+ return uiTableValueTypeString;
+ case 1:
+ return uiTableValueTypeString;
+ default:
+ return uiTableValueTypeString;
+ }
+}
+
+static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) {
+ return ptp_object_service_length(
+ my_handler(mh)->r,
+ my_handler(mh)->oc
+ );
+}
+
+static uiTableValue *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int column)
+{
+ struct MyTableModelHandler *h = my_handler(mh);
+
+ if (column == 0) {
+ struct PtpObjectInfo *oi = ptp_object_service_get_index(h->r, h->oc, row);
+ return uiNewTableValueString(oi->filename);
+ }
+
+ return uiNewTableValueString("TODO");
+}
+
+static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int column, const uiTableValue *val) {
+ // This list is not editable
+}
+
+uiControl *create_files_tab(struct PtpRuntime *r, struct PtpObjectCache *oc) {
+ struct MyTableModelHandler *mh = (struct MyTableModelHandler *)malloc(sizeof(struct MyTableModelHandler));
+ mh->mh.NumColumns = modelNumColumns;
+ mh->mh.ColumnType = modelColumnType;
+ mh->mh.NumRows = modelNumRows;
+ mh->mh.CellValue = modelCellValue;
+ mh->mh.SetCellValue = modelSetCellValue;
+ mh->r = r;
+ mh->oc = oc;
+
+ uiTableModel *m = uiNewTableModel((uiTableModelHandler *)mh);
+
+ uiTableParams p = {
+ .Model = m,
+ .RowBackgroundColorModelColumn = 3,
+ };
+
+ uiTable *t = uiNewTable(&p);
+
+ uiTableAppendTextColumn(t, "Filename", 0, uiTableModelColumnNeverEditable, NULL);
+ uiTableAppendTextColumn(t, "Last Modified", 1, uiTableModelColumnNeverEditable, NULL);
+
+ return uiControl(t);
+}
diff --git a/desktop/libuilua.c b/desktop/libuilua.c
deleted file mode 100644
index 4037c95..0000000
--- a/desktop/libuilua.c
+++ /dev/null
@@ -1,837 +0,0 @@
-
-#include
-
-#include
-#include
-#include
-
-#include "ui.h"
-
-
-/*
- * Boilerplate macros
- */
-
-struct wrap {
- uiControl *control;
-};
-
-
-#define CAST_ARG(n, type) ui ## type(((struct wrap *)lua_touserdata(L, n))->control)
-
-#define RETURN_SELF lua_pushvalue(L, 1); return 1;
-
-#define CREATE_META(n) \
- luaL_newmetatable(L, "libui." #n); \
- luaL_setfuncs(L, meta_ ## n, 0);
-
-#define CREATE_OBJECT(t, c) \
- struct wrap *w = lua_newuserdata(L, sizeof(struct wrap)); \
- w->control = uiControl(c); \
- lua_newtable(L); \
- luaL_getmetatable(L, "libui." #t); \
- lua_setfield(L, -2, "__index"); \
- lua_pushcfunction(L, l_gc); \
- lua_setfield(L, -2, "__gc"); \
- lua_setmetatable(L, -2);
-
-static void create_callback_data(lua_State *L, int n)
-{
- /* Push registery key: userdata pointer to control */
-
- lua_pushlightuserdata(L, CAST_ARG(1, Control));
-
- /* Create table with callback data */
-
- lua_newtable(L);
-
- lua_pushstring(L, "foo");
- lua_pushinteger(L, 123);
- lua_settable(L, -3);
-
- lua_pushvalue(L, 1);
- lua_setfield(L, -2, "udata");
- lua_pushvalue(L, 2);
- lua_setfield(L, -2, "fn");
- //lua_pushinteger(L, 1234);
- lua_pushvalue(L, 3);
- lua_setfield(L, -2, "data");
-
- /* Store in registry */
-
- lua_settable(L, LUA_REGISTRYINDEX);
-
-}
-
-static void callback(lua_State *L, void *control)
-{
- /* Find table with callback data in registry */
-
- lua_pushlightuserdata(L, control);
- lua_gettable(L, LUA_REGISTRYINDEX);
-
- /* Get function, control userdata and callback data */
-
- luaL_checktype(L, 1, LUA_TTABLE);
-
- lua_pushstring(L, "fn");
- lua_gettable(L, -2);
- luaL_checktype(L, -1, LUA_TFUNCTION);
-
- lua_pushstring(L, "udata");
- lua_gettable(L, -3);
- //luaL_checktype(L, -1, LUA_TUSERDATA);
-
- lua_pushstring(L, "data");
- lua_gettable(L, -4);
- //luaL_checktype(L, -1, LUA_TUSERDATA);
-
- /* Call function */
-
- lua_call(L, 2, 0);
-
- /* Cleanup stack */
-
- lua_pop(L, 1);
-}
-
-
-int l_gc(lua_State *L)
-{
- return 0;
-
- struct wrap *w = lua_touserdata(L, 1);
- uint32_t s = w->control->TypeSignature;
- printf("gc %p %c%c%c%c\n", w->control, s >> 24, s >> 16, s >> 8, s >> 0);
-
- uiControl *control = CAST_ARG(1, Control);
- uiControl *parent = uiControlParent(control);
-
- if(parent) {
- if(parent->TypeSignature == 0x57696E64) {
- //uiWindowSetChild(uiWindow(parent), NULL);
- }
- if(parent->TypeSignature == 0x47727062) {
- //uiGroupSetChild(uiWindow(parent), NULL);
- }
- }
- //uiControlDestroy(control);
-
- return 0;
-}
-
-
-/*
- * Area
- */
-
-int l_NewArea(lua_State *L)
-{
- static struct uiAreaHandler ah;
- CREATE_OBJECT(Area, uiNewArea(&ah));
- return 1;
-}
-
-int l_AreaSetSize(lua_State *L)
-{
- uiAreaSetSize(CAST_ARG(1, Area), luaL_checknumber(L, 2), luaL_checknumber(L, 3));
- RETURN_SELF;
-}
-
-static struct luaL_Reg meta_Area[] = {
- { "SetSize", l_AreaSetSize },
- { NULL }
-};
-
-
-/*
- * Box
- */
-
-int l_NewVerticalBox(lua_State *L)
-{
- CREATE_OBJECT(Box, uiNewVerticalBox());
- return 1;
-}
-
-int l_NewHorizontalBox(lua_State *L)
-{
- CREATE_OBJECT(Box, uiNewHorizontalBox());
- return 1;
-}
-
-int l_BoxAppend(lua_State *L)
-{
- int n = lua_gettop(L);
- int stretchy = 0;
-
- if(lua_isnumber(L, n) || lua_isboolean(L, n)) {
- stretchy = lua_toboolean(L, n);
- }
-
- int i;
-
- for(i=2; i<=n; i++) {
- if(lua_isuserdata(L, i)) {
- uiBoxAppend(CAST_ARG(1, Box), CAST_ARG(i, Control), stretchy);
- lua_getmetatable(L, 1);
- lua_pushvalue(L, i);
- luaL_ref(L, -2);
- }
- }
- RETURN_SELF;
-}
-
-int l_BoxPadded(lua_State *L)
-{
- lua_pushnumber(L, uiBoxPadded(CAST_ARG(1, Box)));
- return 1;
-}
-
-int l_BoxSetPadded(lua_State *L)
-{
- uiBoxSetPadded(CAST_ARG(1, Box), luaL_checknumber(L, 2));
- RETURN_SELF;
-}
-
-
-static struct luaL_Reg meta_Box[] = {
- { "Append", l_BoxAppend },
- { "Padded", l_BoxPadded },
- { "SetPadded", l_BoxSetPadded },
- { NULL }
-};
-
-
-/*
- * Button
- */
-
-int l_NewButton(lua_State *L)
-{
- CREATE_OBJECT(Button, uiNewButton(
- luaL_checkstring(L, 1)
- ));
- return 1;
-}
-
-static void on_button_clicked(uiButton *b, void *data)
-{
- callback(data, b);
-}
-
-int l_ButtonSetText(lua_State *L)
-{
- uiButtonSetText(CAST_ARG(1, Button), luaL_checkstring(L, 2));
- RETURN_SELF;
-}
-
-int l_ButtonOnClicked(lua_State *L)
-{
- uiButtonOnClicked(CAST_ARG(1, Button), on_button_clicked, L);
- create_callback_data(L, 1);
- RETURN_SELF;
-}
-
-static struct luaL_Reg meta_Button[] = {
- { "SetText", l_ButtonSetText },
- { "OnClicked", l_ButtonOnClicked },
- { NULL }
-};
-
-
-/*
- * Checkbox
- */
-
-int l_NewCheckbox(lua_State *L)
-{
- CREATE_OBJECT(Checkbox, uiNewCheckbox(
- luaL_checkstring(L, 1)
- ));
- return 1;
-}
-
-static void on_checkbox_toggled(uiCheckbox *c, void *data)
-{
- callback(data, c);
-}
-
-int l_CheckboxSetText(lua_State *L)
-{
- uiCheckboxSetText(CAST_ARG(1, Checkbox), luaL_checkstring(L, 2));
- RETURN_SELF;
-}
-
-int l_CheckboxOnToggled(lua_State *L)
-{
- uiCheckboxOnToggled(CAST_ARG(1, Checkbox), on_checkbox_toggled, L);
- create_callback_data(L, 1);
- RETURN_SELF;
-}
-
-static struct luaL_Reg meta_Checkbox[] = {
- { "SetText", l_CheckboxSetText },
- { "OnToggled", l_CheckboxOnToggled },
- { NULL }
-};
-
-
-/*
- * Combobox
- */
-
-int l_NewCombobox(lua_State *L)
-{
- CREATE_OBJECT(Combobox, uiNewCombobox());
- return 1;
-}
-
-int l_NewEditableCombobox(lua_State *L)
-{
- CREATE_OBJECT(Combobox, uiNewEditableCombobox());
- return 1;
-}
-
-static void on_combobox_selected(uiCombobox *c, void *data)
-{
- callback(data, c);
-}
-
-int l_ComboboxAppend(lua_State *L)
-{
- int n = lua_gettop(L);
- int i;
- for(i=2; i<=n; i++) {
- const char *text = luaL_checkstring(L, i);
- uiComboboxAppend(CAST_ARG(1, Combobox), text);
- }
- RETURN_SELF;
-}
-
-int l_ComboboxOnToggled(lua_State *L)
-{
- uiComboboxOnSelected(CAST_ARG(1, Combobox), on_combobox_selected, L);
- create_callback_data(L, 1);
- RETURN_SELF;
-}
-
-static struct luaL_Reg meta_Combobox[] = {
- { "Append", l_ComboboxAppend },
- { "OnToggled", l_ComboboxOnToggled },
- { NULL }
-};
-
-
-/*
- * Control
- */
-
-int l_ControlShow(lua_State *L)
-{
- uiControlShow(CAST_ARG(1, Control));
- RETURN_SELF;
-}
-
-
-int l_ControlDestroy(lua_State *L)
-{
- printf("destroy not implemented, garbage collection needs to be implemented\n");
- uiControlDestroy(CAST_ARG(1, Control));
- return 0;
-}
-
-
-
-/*
- * Date/Timepicker
- */
-
-int l_NewDateTimePicker(lua_State *L)
-{
- CREATE_OBJECT(DateTimePicker, uiNewDateTimePicker());
- return 1;
-}
-
-int l_NewDatePicker(lua_State *L)
-{
- CREATE_OBJECT(DateTimePicker, uiNewDatePicker());
- return 1;
-}
-
-int l_NewTimePicker(lua_State *L)
-{
- CREATE_OBJECT(DateTimePicker, uiNewTimePicker());
- return 1;
-}
-
-static struct luaL_Reg meta_DateTimePicker[] = {
- { NULL }
-};
-
-
-
-
-/*
- * Group
- */
-
-int l_NewGroup(lua_State *L)
-{
- CREATE_OBJECT(Group, uiNewGroup(
- luaL_checkstring(L, 1)
- ));
- return 1;
-}
-
-int l_GroupTitle(lua_State *L)
-{
- lua_pushstring(L, uiGroupTitle(CAST_ARG(1, Group)));
- return 1;
-}
-
-int l_GroupSetTitle(lua_State *L)
-{
- const char *title = luaL_checkstring(L, 2);
- uiGroupSetTitle(CAST_ARG(1, Group), title);
- RETURN_SELF;
-}
-
-int l_GroupSetChild(lua_State *L)
-{
- uiGroupSetChild(CAST_ARG(1, Group), CAST_ARG(2, Control));
- lua_getmetatable(L, 1);
- lua_pushvalue(L, 2);
- lua_pushboolean(L, 1);
- lua_settable(L, -3);
- RETURN_SELF;
-}
-
-int l_GroupMargined(lua_State *L)
-{
- lua_pushnumber(L, uiGroupMargined(CAST_ARG(1, Group)));
- return 1;
-}
-
-int l_GroupSetMargined(lua_State *L)
-{
- uiGroupSetMargined(CAST_ARG(1, Group), luaL_checknumber(L, 2));
- RETURN_SELF;
-}
-
-static struct luaL_Reg meta_Group[] = {
- { "Title", l_GroupTitle },
- { "SetTitle", l_GroupSetTitle },
- { "SetChild", l_GroupSetChild },
- { "Margined", l_GroupMargined },
- { "SetMargined", l_GroupSetMargined },
- { NULL }
-};
-
-
-/*
- * Label
- */
-
-int l_NewLabel(lua_State *L)
-{
- CREATE_OBJECT(Label, uiNewLabel(
- luaL_checkstring(L, 1)
- ));
- return 1;
-}
-
-int l_LabelText(lua_State *L)
-{
- lua_pushstring(L, uiLabelText(CAST_ARG(1, Label)));
- return 1;
-}
-
-int l_LabelSetText(lua_State *L)
-{
- uiLabelSetText(CAST_ARG(1, Label), luaL_checkstring(L, 2));
- RETURN_SELF;
-}
-
-
-static struct luaL_Reg meta_Label[] = {
- { "Text", l_LabelText },
- { "SetText", l_LabelSetText },
- { NULL }
-};
-
-
-
-
-/*
- * ProgressBar
- */
-
-int l_NewProgressBar(lua_State *L)
-{
- CREATE_OBJECT(ProgressBar, uiNewProgressBar());
- return 1;
-}
-
-int l_ProgressBarSetValue(lua_State *L)
-{
- double value = luaL_checknumber(L, 2);
- uiProgressBarSetValue(CAST_ARG(1, ProgressBar), value);
- RETURN_SELF;
-}
-
-static struct luaL_Reg meta_ProgressBar[] = {
- { "SetValue", l_ProgressBarSetValue },
- { NULL }
-};
-
-
-/*
- * RadioButtons
- */
-
-int l_NewRadioButtons(lua_State *L)
-{
- CREATE_OBJECT(RadioButtons, uiNewRadioButtons());
- return 1;
-}
-
-
-int l_RadioButtonsAppend(lua_State *L)
-{
- int n = lua_gettop(L);
- int i;
- for(i=2; i<=n; i++) {
- const char *text = luaL_checkstring(L, i);
- uiRadioButtonsAppend(CAST_ARG(1, RadioButtons), text);
- }
- RETURN_SELF;
-}
-
-
-static struct luaL_Reg meta_RadioButtons[] = {
- { "Append", l_RadioButtonsAppend },
- { NULL }
-};
-
-
-
-
-/*
- * Separator
- */
-
-int l_NewHorizontalSeparator(lua_State *L)
-{
- CREATE_OBJECT(Separator, uiNewHorizontalSeparator());
- return 1;
-}
-
-static struct luaL_Reg meta_Separator[] = {
- { NULL }
-};
-
-
-/*
- * Slider
- */
-
-int l_NewSlider(lua_State *L)
-{
- CREATE_OBJECT(Slider, uiNewSlider(
- luaL_checknumber(L, 1),
- luaL_checknumber(L, 2)
- ));
- return 1;
-}
-
-int l_SliderValue(lua_State *L)
-{
- lua_pushnumber(L, uiSliderValue(CAST_ARG(1, Slider)));
- return 1;
-}
-
-int l_SliderSetValue(lua_State *L)
-{
- double value = luaL_checknumber(L, 2);
- uiSliderSetValue(CAST_ARG(1, Slider), value);
- RETURN_SELF;
-}
-
-static void on_slider_changed(uiSlider *b, void *data)
-{
- callback(data, b);
-}
-
-int l_SliderOnChanged(lua_State *L)
-{
- uiSliderOnChanged(CAST_ARG(1, Slider), on_slider_changed, L);
- create_callback_data(L, 1);
- RETURN_SELF;
-}
-
-static struct luaL_Reg meta_Slider[] = {
- { "Value", l_SliderValue },
- { "SetValue", l_SliderSetValue },
- { "OnChanged", l_SliderOnChanged },
- { NULL }
-};
-
-
-/*
- * Spinbox
- */
-
-int l_NewSpinbox(lua_State *L)
-{
- CREATE_OBJECT(Spinbox, uiNewSpinbox(
- luaL_checknumber(L, 1),
- luaL_checknumber(L, 2)
- ));
- return 1;
-}
-
-int l_SpinboxValue(lua_State *L)
-{
- lua_pushnumber(L, uiSpinboxValue(CAST_ARG(1, Spinbox)));
- return 1;
-}
-
-int l_SpinboxSetValue(lua_State *L)
-{
- double value = luaL_checknumber(L, 2);
- uiSpinboxSetValue(CAST_ARG(1, Spinbox), value);
- RETURN_SELF;
-}
-
-static void on_spinbox_changed(uiSpinbox *b, void *data)
-{
- callback(data, b);
-}
-
-int l_SpinboxOnChanged(lua_State *L)
-{
- uiSpinboxOnChanged(CAST_ARG(1, Spinbox), on_spinbox_changed, L);
- create_callback_data(L, 1);
- RETURN_SELF;
-}
-
-static struct luaL_Reg meta_Spinbox[] = {
- { "Value", l_SpinboxValue },
- { "SetValue", l_SpinboxSetValue },
- { "OnChanged", l_SpinboxOnChanged },
- { NULL }
-};
-
-
-/*
- * Tab
- */
-
-int l_NewTab(lua_State *L)
-{
- CREATE_OBJECT(Tab, uiNewTab());
- return 1;
-}
-
-int l_TabAppend(lua_State *L)
-{
- int n = lua_gettop(L);
- int i;
- for(i=2; i<=n; i+=2) {
- uiTabAppend(CAST_ARG(1, Tab), luaL_checkstring(L, i+0), CAST_ARG(i+1, Control));
- lua_getmetatable(L, 1);
- lua_pushvalue(L, i+1);
- luaL_ref(L, -2);
- }
- RETURN_SELF;
-}
-
-static struct luaL_Reg meta_Tab[] = {
- { "Append", l_TabAppend },
- { NULL }
-};
-
-
-
-
-
-/*
- * Window
- */
-
-int onClosing(uiWindow *w, void *data)
-{
- uiQuit();
- return 1;
-}
-
-static int onShouldQuit(void *data)
-{
- uiWindow *mainwin = uiWindow(data);
- uiControlDestroy(uiControl(mainwin));
- return 0;
-}
-
-int l_NewWindow(lua_State *L)
-{
- CREATE_OBJECT(Window, uiNewWindow(
- luaL_checkstring(L, 1),
- luaL_checknumber(L, 2),
- luaL_checknumber(L, 3),
- lua_toboolean(L, 4)
- ));
-
- uiWindowOnClosing((uiWindow *)w->control, onShouldQuit, w->control);
- uiWindowSetMargined((uiWindow *)w->control, 1);
-
- return 1;
-}
-
-int l_WindowSetChild(lua_State *L)
-{
- uiWindowSetChild(CAST_ARG(1, Window), CAST_ARG(2, Control));
- lua_getmetatable(L, 1);
- lua_pushvalue(L, 2);
- lua_pushboolean(L, 1);
- lua_settable(L, -3);
- RETURN_SELF;
-}
-
-static int on_window_closing(uiWindow *w, void *data)
-{
- callback(data, w);
- return 0;
-}
-
-int l_WindowOnClosing(lua_State *L)
-{
- uiWindowOnClosing(CAST_ARG(1, Window), on_window_closing, L);
- create_callback_data(L, 1);
- RETURN_SELF;
-}
-
-int l_WindowSetMargined(lua_State *L)
-{
- uiWindowSetMargined(CAST_ARG(1, Window), luaL_checknumber(L, 2));
- RETURN_SELF;
-}
-
-int l_MsgBox(lua_State *L)
-{
- uiMsgBox(CAST_ARG(1, Window), luaL_checkstring(L, 2), luaL_checkstring(L, 3));
- RETURN_SELF;
-}
-
-static struct luaL_Reg meta_Window[] = {
- { "OnClosing", l_WindowOnClosing },
- { "SetChild", l_WindowSetChild },
- { "SetMargined", l_WindowSetMargined },
- { "Show", l_ControlShow },
- { "Destroy", l_ControlDestroy },
- { "MsgBox", l_MsgBox},
- { NULL }
-};
-
-
-
-/*
- * Various top level
- */
-
-int l_Init(lua_State *L)
-{
- uiInitOptions o;
-
- memset(&o, 0, sizeof (uiInitOptions));
-
- const char *err = uiInit(&o);
-
- lua_pushstring(L, err);
- return 1;
-}
-
-int l_Uninit(lua_State *L)
-{
- uiUninit();
- return 0;
-}
-
-int l_Main(lua_State *L)
-{
- uiMain();
- return 0;
-}
-
-int l_MainStep(lua_State *L)
-{
- int r = uiMainStep(lua_toboolean(L, 1));
- lua_pushnumber(L, r);
- return 1;
-}
-
-int l_Quit(lua_State *L)
-{
- uiQuit();
- return 0;
-}
-
-static struct luaL_Reg lui_table[] = {
-
- { "Init", l_Init },
- { "Uninit", l_Uninit },
- { "Main", l_Main },
- { "MainStep", l_MainStep },
- { "Quit", l_Quit },
-
- { "NewArea", l_NewArea },
- { "NewButton", l_NewButton },
- { "NewCheckbox", l_NewCheckbox },
- { "NewCombobox", l_NewCombobox },
- { "NewDateTimePicker", l_NewDateTimePicker },
- { "NewDatePicker", l_NewDatePicker },
- { "NewTimePicker", l_NewTimePicker },
- { "NewEditableCombobox", l_NewEditableCombobox },
- { "NewGroup", l_NewGroup },
- { "NewHorizontalBox", l_NewHorizontalBox },
- { "NewHorizontalSeparator", l_NewHorizontalSeparator },
- { "NewLabel", l_NewLabel },
- { "NewProgressBar", l_NewProgressBar },
- { "NewRadioButtons", l_NewRadioButtons },
- { "NewSlider", l_NewSlider },
- { "NewSpinbox", l_NewSpinbox },
- { "NewTab", l_NewTab },
- { "NewVerticalBox", l_NewVerticalBox },
- { "NewWindow", l_NewWindow },
-
-
- { NULL }
-};
-
-
-int luaopen_libuilua(lua_State *L)
-{
-
-
- CREATE_META(Area)
- CREATE_META(Box)
- CREATE_META(Button)
- CREATE_META(Checkbox)
- CREATE_META(Combobox)
- CREATE_META(DateTimePicker)
- CREATE_META(Group)
- CREATE_META(Label)
- CREATE_META(ProgressBar)
- CREATE_META(RadioButtons)
- CREATE_META(Separator)
- CREATE_META(Slider)
- CREATE_META(Spinbox)
- CREATE_META(Tab)
- CREATE_META(Window)
- luaL_newlib(L, lui_table);
- return 1;
-}
-
-/*
- * End
- */
diff --git a/desktop/main.c b/desktop/main.c
index d254246..9589f4c 100644
--- a/desktop/main.c
+++ b/desktop/main.c
@@ -16,12 +16,6 @@
#include
#include "desktop.h"
-static struct PtpRuntime *ptp;
-
-struct PtpRuntime *ptp_get() {
- return ptp;
-}
-
void ptp_verbose_log(char *fmt, ...) {
printf("PTP: ");
va_list args;
@@ -58,21 +52,6 @@ int app_check_thread_cancel(void) {
return 0;
}
-void *fudge_usb_connect(void *arg) {
- struct PtpRuntime *r = ptp_get();
- int rc;
- if (ptp_device_init(r)) {
- puts("Device connection error");
- return 0;
- }
- fuji_reset_ptp(r);
-
- app_print("Hello, World");
-
- pthread_exit(NULL);
- return NULL;
-}
-
int fuji_discover_ask_connect(void *arg, struct DiscoverInfo *info) {
// Ask if we want to connect?
return 1;
@@ -82,9 +61,28 @@ int fuji_discovery_check_cancel(void *arg) {
return 0;
}
+int ptp_list_devices(void) {
+ struct PtpRuntime *r = ptp_new(PTP_USB);
+
+ struct PtpDeviceEntry *list = ptpusb_device_list(r);
+
+ for (; list != NULL; list = list->next) {
+ printf("product id: %04x\n", list->product_id);
+ printf("vendor id: %04x\n", list->vendor_id);
+ printf("Vendor friendly name: '%s'\n", list->manufacturer);
+ printf("Model friendly name: '%s'\n", list->name);
+ }
+
+ return 0;
+}
+
int main(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "--test-wifi")) {
+ if (!strcmp(argv[i], "--list")) {
+ return ptp_list_devices();
+ } else if (!strcmp(argv[i], "--script")) {
+ return fuji_connect_run_script(argv[i + 1]);
+ } else if (!strcmp(argv[i], "--test-wifi")) {
int rc = fudge_test_all_cameras();
plat_dbg("Result: %d\n", rc);
return rc;
diff --git a/desktop/os.c b/desktop/os.c
new file mode 100644
index 0000000..d52deb4
--- /dev/null
+++ b/desktop/os.c
@@ -0,0 +1,23 @@
+#include
+#include
+#include
+
+void app_get_file_path(char buffer[256], const char *filename) {
+ snprintf(buffer, 256, "%s", filename);
+}
+
+void app_get_tether_file_path(char buffer[256]) {
+ snprintf(buffer, 256, "TETHER.JPG");
+}
+
+int app_get_os_network_handle(struct NetworkHandle *h) {
+ return 0;
+}
+
+int app_get_wifi_network_handle(struct NetworkHandle *h) {
+ return -1;
+}
+
+int app_bind_socket_to_network(int fd, struct NetworkHandle *h) {
+ return 0;
+}
diff --git a/desktop/safety.h b/desktop/safety.h
new file mode 100644
index 0000000..a5ea014
--- /dev/null
+++ b/desktop/safety.h
@@ -0,0 +1,25 @@
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+
+#undef uiControl
+#undef uiButton
+#undef uiLabel
+
+#define uiControl(this) _Generic((this), \
+ uiButton *: (uiControl *)this, \
+ uiBox *: (uiControl *)this, \
+ uiWindow *: (uiControl *)this, \
+ uiProgressBar *: (uiControl *)this, \
+ uiGroup *: (uiControl *)this, \
+ uiLabel *: (uiControl *)this, \
+ uiMultilineEntry *: (uiControl *)this, \
+ uiTab *: (uiControl *)this, \
+ uiControl *: this \
+ )
+
+#define uiButton(this) _Generic((this), \
+ uiControl *: (uiButton *)this)
+
+#define uiLabel(this) _Generic((this), \
+ uiControl *: (uiLabel *)this)
+
+#endif
diff --git a/desktop/test.lua b/desktop/test.lua
new file mode 100644
index 0000000..6454921
--- /dev/null
+++ b/desktop/test.lua
@@ -0,0 +1 @@
+print(fuji.test())
diff --git a/desktop/ui.c b/desktop/ui.c
index 42d5e7a..2cf792f 100644
--- a/desktop/ui.c
+++ b/desktop/ui.c
@@ -1,57 +1,65 @@
+// 2024 Fudge desktop frontend
#include
#include
#include
#include
#include
#include
-#include
#include
+#include "safety.h"
#include
#include "desktop.h"
-int luaopen_libuilua(lua_State *L);
-
-void uiToast() {}
+void uiToast(const char *format, ...) {}
struct App {
int is_opened;
uiWindow *main_win;
- uiLabel *main_log;
+ uiMultilineEntry *main_log;
uiMultilineEntry *script_box;
uiMultilineEntry *connect_entry;
+ uiLabel *bottom_status;
+ uiTab *right_tab;
}app = {0};
-struct PtpRuntime *luaptp_get_runtime(lua_State *L) {
- return ptp_get();
-}
-
void app_send_cam_name(const char *name) {
if (!app.is_opened) {
return;
}
- char buffer[64];
- sprintf(buffer, "Fudge - %s", name);
- uiWindowSetTitle(app.main_win, buffer);
+ if (name) {
+ char buffer[64];
+ sprintf(buffer, "Fudge - Connected to %s", name);
+ uiWindowSetTitle(app.main_win, buffer);
+ sprintf(buffer, "Connected to %s", name);
+ uiLabelSetText(app.bottom_status, buffer);
+ } else {
+ uiWindowSetTitle(app.main_win, "Fudge");
+ uiLabelSetText(app.bottom_status, "Not connected to any camera");
+ }
}
void fuji_discovery_update_progress(void *arg, int progress) {
- // ...
+ if (!app.is_opened) return;
}
void app_downloading_file(const struct PtpObjectInfo *oi) {
+ if (!app.is_opened) return;
// ...
}
void app_downloaded_file(const struct PtpObjectInfo *oi, const char *path) {
+ if (!app.is_opened) return;
// ...
}
void app_increment_progress_bar(int read) {
+ if (!app.is_opened) return;
// ...
}
void app_report_download_speed(long time, size_t size) {
+ if (!app.is_opened) return;
// ...
}
@@ -73,8 +81,14 @@ void tester_fail(char *fmt, ...) {
printf("FAIL: %s\n", buffer);
}
-static void print_thread(char *buffer) {
- uiMultilineEntryAppend(app.connect_entry, buffer);
+static void clear_thread(void *arg) {
+ uiMultilineEntrySetText(app.main_log, "");
+}
+
+static void print_thread(void *buffer) {
+ uiMultilineEntryAppend(app.main_log, (const char *)buffer);
+ free(buffer);
+ uiMultilineEntryAppend(app.main_log, "\n");
}
void app_print(char *fmt, ...) {
@@ -84,15 +98,17 @@ void app_print(char *fmt, ...) {
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
if (app.is_opened) {
- uiMultilineEntryAppend(app.connect_entry, buffer);
- //uiLabelSetText(app.main_log, buffer);
uiQueueMain(print_thread, (void *)strdup(buffer));
} else {
puts(buffer);
}
}
-static char *read_file(char *filename) {
+void app_log_clear(void) {
+ uiQueueMain(clear_thread, NULL);
+}
+
+static char *read_file(const char *filename) {
FILE* file = fopen(filename, "rb");
if (!file) return NULL;
@@ -112,6 +128,7 @@ static char *read_file(char *filename) {
static int onClosing(uiWindow *w, void *data)
{
+ fudge_disconnect_all();
uiQuit();
return 1;
}
@@ -123,73 +140,32 @@ static int onShouldQuit(void *data)
return 1;
}
-static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m)
-{
- return 3;
+static uiControl *files_tab(void) {
+ return uiControl(uiNewLabel("TODO..."));
}
-static uiTableValueType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column)
-{
- return uiTableValueTypeString;
-}
-
-static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m)
-{
- return 100;
-}
+static uiControl *general_tab(void) {
+ uiBox *box = uiNewVerticalBox();
+ uiBoxAppend(box, uiControl(uiNewLabel("Hello")), 0);
-static uiTableValue *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int column)
-{
- switch (column) {
- case 0: return uiNewTableValueString("TEST.JPG");
- default: return uiNewTableValueString("Date");
+ {
+ uiGroup *g = uiNewGroup("Camera Settings");
+ uiButton *btn = uiNewButton("Backup settings");
+ uiGroupSetChild(g, uiControl(btn));
+ uiBoxAppend(box, uiControl(g), 0);
}
-}
-
-static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int column, const uiTableValue *val)
-{
+ return uiControl(box);
}
-static uiControl *files_tab() {
-
- static uiTableModelHandler mh = {
- .NumColumns = modelNumColumns,
- .ColumnType = modelColumnType,
- .NumRows = modelNumRows,
- .CellValue = modelCellValue,
- .SetCellValue = modelSetCellValue,
- };
-
- uiTableModel *m = uiNewTableModel(&mh);
-
- uiTableParams p = {
- .Model = m,
- .RowBackgroundColorModelColumn = 3,
- };
-
- uiTable *t = uiNewTable(&p);
-
- uiTableAppendTextColumn(t, "Filename",
- 0, uiTableModelColumnNeverEditable, NULL);
-
- uiTableAppendTextColumn(t, "Last Modified",
- 1, uiTableModelColumnNeverEditable, NULL);
-
- return uiControl(t);
-}
-
-extern void *fudge_usb_connect(void *arg);
-
static void usb_connect(uiButton *btn, void *data) {
pthread_t thread;
-
- pthread_create(&thread, 0, fudge_usb_connect, NULL);
+ pthread_create(&thread, 0, fudge_usb_connect_thread, NULL);
}
-int cam_lua_setup(lua_State *L) {
- luaL_requiref(L, "ui", luaopen_libuilua, 1);
- return 0;
+static void usb_connect_menu(uiMenuItem *i, uiWindow *w, void *data) {
+ pthread_t thread;
+ pthread_create(&thread, 0, fudge_usb_connect_thread, NULL);
}
static void script_run(uiMenuItem *sender, uiWindow *window, void *senderData) {
@@ -201,7 +177,22 @@ static void script_run(uiMenuItem *sender, uiWindow *window, void *senderData) {
uiFreeText(file);
}
-uiControl *editor_tab() {
+void app_update_connected_status_(void *arg) {
+ uiTab *tab = app.right_tab;
+ if ((uintptr_t)arg) {
+ uiTabInsertAt(tab, "General", 0, general_tab());
+ uiTabSetSelected(tab, 0);
+ } else {
+ uiTabSetSelected(tab, 1);
+ uiTabDelete(tab, 0);
+ app_send_cam_name(NULL);
+ }
+}
+void app_update_connected_status(int connected) {
+ uiQueueMain(app_update_connected_status_, (void *)(uintptr_t)connected);
+}
+
+uiControl *editor_tab(void) {
char *file = read_file("../android/app/src/main/assets/script.lua");
if (file == NULL) file = "null";
app.script_box = uiNewNonWrappingMultilineEntry();
@@ -209,61 +200,138 @@ uiControl *editor_tab() {
return uiControl(app.script_box);
}
-uiControl *connect_tab() {
+uiControl *connect_tab(void) {
uiMultilineEntry *entry = uiNewMultilineEntry();
- uiEntrySetReadOnly(entry, 1);
+ uiMultilineEntrySetReadOnly(entry, 1);
app.connect_entry = entry;
return (uiControl *)entry;
}
-static uiControl *fudge_screen() {
+uiControl *create_discovery_ui(void) {
+ uiGroup *discovery = uiNewGroup("Searching for a camera...");
+ uiBox *inner = uiNewVerticalBox();
+
+ uiBoxAppend(inner, uiControl(uiNewLabel("- PC AutoSave")), 0);
+ uiBoxAppend(inner, uiControl(uiNewLabel("- Wireless Tether")), 0);
+
+ uiProgressBar *bar = uiNewProgressBar();
+ uiProgressBarSetValue(bar, -1);
+ uiBoxAppend(inner, uiControl(bar), 0);
+
+ uiGroupSetChild(discovery, uiControl(inner));
+ return uiControl(discovery);
+}
+
+uiControl *about_tab(void) {
uiBox *box = uiNewVerticalBox();
+ //uiBoxSetPadded(box, 1);
+
+ {
+ uiGroup *g = uiNewGroup("Fudge");
+ uiMultilineEntry *text = uiNewMultilineEntry();
+ uiMultilineEntrySetReadOnly(text, 1);
+ uiMultilineEntrySetText(text,
+ "Connect to a camera to get started.\n"
+ "For questions, feature requests, or bug reports, shoot me an email or file an issue on the Github page.\n"
+ "- brikbusters@gmail.com\n"
+ "- https://github.com/petabyt/fudge\n"
+ );
+ uiGroupSetChild(g, uiControl(text));
+ uiBoxAppend(box, uiControl(g), 1);
+ }
+
+ {
+ uiGroup *g = uiNewGroup("Credits");
+ uiMultilineEntry *text = uiNewMultilineEntry();
+ uiMultilineEntrySetReadOnly(text, 1);
+ uiMultilineEntrySetText(text,
+ "Created by Daniel Cook (danielc.dev)\n"
+ "Libraries:\n"
+ "- https://github.com/libui-ng/libui-ng (MIT License)\n"
+ "- https://github.com/petabyt/libwpd\n"
+ "- https://github.com/petabyt/camlib\n"
+ "- Lua 5.3 (MIT License)\n"
+ );
+ uiGroupSetChild(g, uiControl(text));
+ uiBoxAppend(box, uiControl(g), 1);
+ }
+
+ return uiControl(box);
+}
+
+static uiControl *fudge_screen(void) {
+ uiBox *box = uiNewHorizontalBox();
uiBoxSetPadded(box, 1);
+ uiBox *left = uiNewVerticalBox();
+ //uiBoxSetPadded(left, 1);
+
uiGroup *connectivity = uiNewGroup("Connect");
{
uiBox *hbox = uiNewVerticalBox();
- uiButton *btn = uiNewButton("Connect to USB");
- uiBoxAppend(hbox, uiControl(btn), 1);
+ //uiBoxSetPadded(hbox, 1);
+ uiButton *btn = uiNewButton("USB");
+ uiControlSetTooltip(uiControl(btn), "Find a camera over USB and connect");
uiButtonOnClicked(btn, usb_connect, NULL);
- uiBoxAppend(hbox, uiControl(uiNewButton("Connect to WiFi")), 1);
+ uiBoxAppend(hbox, uiControl(btn), 1);
+ uiBoxAppend(hbox, uiControl(uiNewButton("WiFi")), 1);
+ uiBoxAppend(hbox, uiControl(uiNewButton("Search for camera")), 1);
uiGroupSetChild(connectivity, uiControl(hbox));
}
- uiBoxAppend(box, uiControl(connectivity), 0);
+ uiBoxAppend(left, uiControl(connectivity), 0);
+
+ app.main_log = uiNewMultilineEntry();
+ uiMultilineEntrySetReadOnly(app.main_log, 1);
+ uiBoxAppend(left, uiControl(app.main_log), 1);
+
+ uiBoxAppend(box, uiControl(left), 1);
- uiGroup *discovery = uiNewGroup("Searching for a camera...");
{
- uiBox *inner = uiNewVerticalBox();
+ uiBox *vbox = uiNewVerticalBox();
+ uiTab *tab = uiNewTab();
+ app.right_tab = tab;
+ //uiTabAppend(tab, "Files", files_tab());
+ //uiTabAppend(tab, "Lua Editor", editor_tab());
+ //uiTabAppend(tab, "Recipes", files_tab());
+ uiTabAppend(tab, "About", about_tab());
+ uiBoxAppend(vbox, uiControl(tab), 1);
+ uiLabel *lbl = uiNewLabel("Not connected to any camera");
+ app.bottom_status = lbl;
+ uiBoxAppend(vbox, uiControl(lbl), 0);
+ uiBoxAppend(box, uiControl(vbox), 1);
+ }
- uiBoxAppend(inner, uiControl(uiNewLabel("- PC AutoSave")), 0);
- uiBoxAppend(inner, uiControl(uiNewLabel("- Wireless Tether")), 0);
+ return (uiControl *)box;
+}
- uiProgressBar *bar = uiNewProgressBar();
- uiProgressBarSetValue(bar, -1);
- uiBoxAppend(inner, uiControl(bar), 0);
+int multiple_cameras_menu(void) {
- uiGroupSetChild(discovery, uiControl(inner));
- }
- uiBoxAppend(box, uiControl(discovery), 0);
+ uiWindow *w = uiNewWindow("Dialog", 500, 100, 0);
+ uiWindowSetMargined(w, 1);
- uiTab *tab = uiNewTab();
- uiTabAppend(tab, "Files", files_tab());
- uiTabAppend(tab, "Lua Editor", editor_tab());
- uiTabAppend(tab, "Recipes", files_tab());
- uiTabAppend(tab, "Developer", files_tab());
- uiBoxAppend(box, uiControl(tab), 1);
- //uiControlHide(uiControl(tab));
+ uiBox *screen = uiNewVerticalBox();
- app.main_log = uiNewLabel("Doing things...");
- uiBoxAppend(box, uiControl(app.main_log), 0);
+ uiBoxAppend(screen, uiControl(uiNewLabel("Choose a camera to connect to")), 1);
- return (uiControl *)box;
+ uiButton *btn = uiNewButton("Fujfilm X-T4");
+ uiBoxAppend(screen, uiControl(btn), 1);
+
+ btn = uiNewButton("Fujifilm X-A2");
+ uiBoxAppend(screen, uiControl(btn), 1);
+
+ btn = uiNewButton("Cancel");
+ uiBoxAppend(screen, uiControl(btn), 1);
+
+ uiWindowSetChild(w, uiControl(screen));
+ uiControlShow(uiControl(w));
+
+ return 0;
}
-int fudge_main_ui(void)
-{
+int fudge_main_ui(void) {
+ app.is_opened = 1;
uiInitOptions options;
const char *err;
uiTab *tab;
@@ -288,18 +356,20 @@ int fudge_main_ui(void)
menu = uiNewMenu("Connect");
item = uiMenuAppendItem(menu, "USB");
- uiMenuItemOnClicked(item, usb_connect, NULL);
+ uiMenuItemOnClicked(item, usb_connect_menu, NULL);
item = uiMenuAppendItem(menu, "WiFi");
}
- app.main_win = uiNewWindow("Fudge", 640, 480, 1);
+ app.main_win = uiNewWindow("Fudge", 1000, 500, 1);
uiWindowOnClosing(app.main_win, onClosing, NULL);
uiOnShouldQuit(onShouldQuit, app.main_win);
uiWindowSetMargined(app.main_win, 1);
uiWindowSetChild(app.main_win, fudge_screen());
-
uiControlShow(uiControl(app.main_win));
+
+ app_print("Fudge desktop pre-release");
+
uiMain();
return 0;
}
diff --git a/desktop/wifi.c b/desktop/wifi.c
index 29ce2a0..e69de29 100644
--- a/desktop/wifi.c
+++ b/desktop/wifi.c
@@ -1,15 +0,0 @@
-#include
-#include
-#include
-
-int app_get_os_network_handle(struct NetworkHandle *h) {
- return 0;
-}
-
-int app_get_wifi_network_handle(struct NetworkHandle *h) {
- return -1;
-}
-
-int app_bind_socket_to_network(int fd, struct NetworkHandle *h) {
- return 0;
-}
diff --git a/desktop/win.c b/desktop/win.c
index 420653d..4d83dcb 100644
--- a/desktop/win.c
+++ b/desktop/win.c
@@ -5,16 +5,14 @@
#include
#include
#include
-#include
-#include
#include
-#include
#include "desktop.h"
+// Windows does not have kill!
void kill(int pid) {}
void network_init() {
- // Windows wants to init this thread for socket stuff
+ // Windows wants to init every thread for socket stuff
WSADATA wsaData = {0};
WSAStartup(MAKEWORD(2, 2), &wsaData);
}
diff --git a/desktop/a.rc b/desktop/win.rc
similarity index 62%
rename from desktop/a.rc
rename to desktop/win.rc
index f5a3d78..04a9f4e 100644
--- a/desktop/a.rc
+++ b/desktop/win.rc
@@ -1,3 +1,3 @@
// this is a UTF-8 file
#pragma code_page(65001)
-1 24 "libui.manifest"
+1 24 "assets/libui.manifest"
diff --git a/lib/camlib b/lib/camlib
index 1c8f3c3..d3903fe 160000
--- a/lib/camlib
+++ b/lib/camlib
@@ -1 +1 @@
-Subproject commit 1c8f3c39846c951bbc578fc510e247f3808742f7
+Subproject commit d3903fe02cd0714ee205c32213b31e82cb77fe02
diff --git a/lib/fuji.c b/lib/fuji.c
index f3879ab..6836d80 100644
--- a/lib/fuji.c
+++ b/lib/fuji.c
@@ -41,13 +41,13 @@ void ptp_report_error(struct PtpRuntime *r, const char *reason, int code) {
// Safely disconnect if intentional
if (code == 0) {
- plat_dbg("Closing session");
+ ptp_verbose_log("Closing session\n");
ptp_close_session(r);
}
r->operation_kill_switch = 1;
- ptp_verbose_log("Goodbye");
+ ptp_verbose_log("Goodbye\n");
if (r->connection_type == PTP_IP_USB) {
// Send Fuji's 'goodbye' packet - we don't care if this fails or not
@@ -936,7 +936,7 @@ int ptp_fuji_get_object_handles(struct PtpRuntime *r, struct PtpArray **a) {
for (int i = 0; i < fuji->num_objects; i++) {
list->data[i] = i + 1;
}
- a[0] = list;
+ (*a) = list;
}
return 0;
}
diff --git a/lib/fuji_lua.c b/lib/fuji_lua.c
new file mode 100755
index 0000000..db1adce
--- /dev/null
+++ b/lib/fuji_lua.c
@@ -0,0 +1,181 @@
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fuji_lua.h"
+
+static int mylua_set_property(lua_State *L) {
+ struct PtpRuntime *r = luaptp_get_runtime(L);
+
+ const char *name = luaL_checkstring(L, 1);
+ int value = lua_tointeger(L, 1);
+
+ int rc = ptp_set_generic_property(r, name, value);
+
+ lua_pushinteger(L, rc);
+
+ return 1;
+}
+
+static int mylua_device_info(lua_State *L) {
+ struct PtpRuntime *r = luaptp_get_runtime(L);
+
+ struct PtpDeviceInfo di;
+ int rc = ptp_get_device_info(r, &di);
+ if (rc) {
+ lua_pushinteger(L, rc);
+ return 1;
+ }
+
+ char buffer[4096];
+ ptp_device_info_json(&di, buffer, sizeof(buffer));
+
+ lua_json_decode(L, buffer, strlen(buffer));
+
+ return 1;
+}
+
+static int mylua_take_picture(lua_State *L) {
+ struct PtpRuntime *r = luaptp_get_runtime(L);
+
+ if (r->di == NULL) return 1;
+
+ int rc = ptp_pre_take_picture(r);
+ if (rc) goto err;
+
+ rc = ptp_take_picture(r);
+ if (rc) goto err;
+
+ err:;
+ lua_pushinteger(L, rc);
+
+ return 1;
+}
+
+static int mylua_send_operation(lua_State *L) {
+ struct PtpRuntime *r = luaptp_get_runtime(L);
+
+ int opcode = lua_tointeger(L, 1);
+ int len = lua_gettop(L);
+
+ if (!lua_istable(L, 2)) {
+ return luaL_error(L, "arg2 expected array");
+ }
+
+ struct PtpCommand cmd;
+ cmd.code = opcode;
+
+ // Read parameters
+ int param_length = 0;
+ if (len >= 2) {
+ param_length = luaL_len(L, 2);
+ for (int i = 1; i <= param_length; ++i) {
+ lua_rawgeti(L, 2, i);
+ cmd.params[i - 1] = luaL_checkinteger(L, -1);
+ lua_pop(L, 1);
+ }
+ cmd.param_length = param_length;
+ }
+
+ // Read payload if provided
+ int data_length = 0;
+ uint8_t *data_array = NULL;
+ if (len >= 3) {
+ data_length = luaL_len(L, 3);
+ data_array = malloc(data_length * sizeof(int));
+ for (int i = 1; i <= data_length; ++i) {
+ lua_rawgeti(L, 3, i);
+ data_array[i - 1] = (uint8_t)luaL_checkinteger(L, -1);
+ lua_pop(L, 1);
+ }
+ }
+
+ int rc = 0;
+ if (data_array == NULL) {
+ rc = ptp_generic_send(r, &cmd);
+ } else {
+ rc = ptp_generic_send_data(r, &cmd, data_array, data_length);
+ }
+
+ lua_newtable(L);
+
+ lua_pushstring(L, "error");
+ lua_pushinteger(L, rc);
+ lua_settable(L, -3);
+
+ lua_pushstring(L, "code");
+ lua_pushinteger(L, ptp_get_return_code(r));
+ lua_settable(L, -3);
+
+ lua_pushstring(L, "payload");
+ lua_newtable(L);
+ for (int i = 0; i < ptp_get_payload_length(r); ++i) {
+ lua_pushinteger(L, i + 1);
+ lua_pushinteger(L, (int)(r->data[i]));
+ lua_settable(L, -3);
+ }
+
+ lua_settable(L, -3);
+
+ lua_pushstring(L, "id");
+ lua_pushinteger(L, ptp_get_last_transaction_id(r));
+ lua_settable(L, -3);
+
+ return 1;
+}
+
+static int mylua_test(lua_State *L) {
+ struct PtpRuntime *r = luaptp_get_runtime(L);
+
+ char *t = "{\"fujifilm\": 123}";
+
+ //json_create_config(L);
+ lua_json_decode(L, t, strlen(t));
+
+ return 1;
+}
+
+static int mylua_connect(lua_State *L) {
+ struct PtpRuntime *r = luaptp_get_runtime(L);
+
+ lua_pushinteger(L, 0);
+
+ return 1;
+}
+
+
+static const luaL_Reg fujilib[] = {
+ {"test", mylua_test},
+#if 0
+ disconnect
+ getTransport
+ getObjectHandles
+#endif
+ {NULL, NULL}
+};
+
+static void new_const(lua_State *L, char *name, int val) {
+ lua_pushstring(L, name);
+ lua_pushnumber(L, val);
+ lua_settable(L, -3);
+}
+
+LUALIB_API int luaopen_fuji(lua_State *L) {
+ luaL_newlib(L, fujilib);
+
+ new_const(L, "FUJI_FEATURE_AUTOSAVE", FUJI_FEATURE_AUTOSAVE);
+ new_const(L, "FUJI_FEATURE_WIRELESS_TETHER", FUJI_FEATURE_WIRELESS_TETHER);
+ new_const(L, "FUJI_FEATURE_WIRELESS_TETHER", FUJI_FEATURE_WIRELESS_TETHER);
+ new_const(L, "FUJI_FEATURE_WIRELESS_COMM", FUJI_FEATURE_WIRELESS_COMM);
+ new_const(L, "FUJI_FEATURE_USB", FUJI_FEATURE_USB);
+ new_const(L, "FUJI_FEATURE_USB_CARD_READER", FUJI_FEATURE_USB_CARD_READER);
+ new_const(L, "FUJI_FEATURE_USB_TETHER_SHOOT", FUJI_FEATURE_USB_TETHER_SHOOT);
+ new_const(L, "FUJI_FEATURE_RAW_CONV", FUJI_FEATURE_RAW_CONV);
+
+ return 1;
+}
diff --git a/lib/fuji_lua.h b/lib/fuji_lua.h
new file mode 100755
index 0000000..dd37145
--- /dev/null
+++ b/lib/fuji_lua.h
@@ -0,0 +1,12 @@
+#define MAX_LUA_CONCURRENT 5
+
+// Must be provided
+//extern int cam_lua_setup(lua_State *L);
+
+LUALIB_API int luaopen_fuji(lua_State *L);
+
+extern struct PtpRuntime *luaptp_get_runtime(lua_State *L);
+
+// lua-cjson
+int luaopen_cjson(lua_State *l);
+void lua_json_decode(lua_State *l, const char *json_text, int json_len);
diff --git a/lib/lua_runtime.c b/lib/lua_runtime.c
new file mode 100755
index 0000000..9cad4ac
--- /dev/null
+++ b/lib/lua_runtime.c
@@ -0,0 +1,173 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include "fuji_lua.h"
+
+static char error_buffer[512] = {0};
+
+static struct CamLuaTasks {
+ int tasks;
+ struct lua_State *L[MAX_LUA_CONCURRENT];
+ int state[MAX_LUA_CONCURRENT];
+}lua_tasks = {0};
+
+const char *cam_lua_get_error(void) {
+ return error_buffer;
+}
+
+int lua_script_run_loop(int id) {
+ if (id >= MAX_LUA_CONCURRENT || id < 0) {
+ snprintf(error_buffer, sizeof(error_buffer), "Script: out of bounds ID: %d\n", id);
+ return 1;
+ }
+
+ struct lua_State *L = lua_tasks.L[(int)id];
+
+ if (L == NULL) return -1;
+
+ lua_getglobal(L, "eventLoop");
+
+ if (lua_isfunction(L, -1)) {
+ if (lua_pcall(L, 0, 0, 0) != LUA_OK) {
+ snprintf(error_buffer, sizeof(error_buffer), "Failed to run Lua: %s", lua_tostring(L, -1));
+ return -1;
+ }
+ } else {
+ snprintf(error_buffer, sizeof(error_buffer), "eventLoop is not a function: %s", lua_tostring(L, -1));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_task_id(lua_State *L) {
+ for (int i = 0; i < lua_tasks.tasks; i++) {
+ if (L == lua_tasks.L[i]) return i;
+ }
+
+ return -1;
+}
+
+static int lua_script_print(lua_State *L) {
+ const char *str = luaL_checkstring(L, 1);
+ ptp_verbose_log("%s\n", str);
+ return 1;
+}
+
+static int lua_script_toast(lua_State *L) {
+ const char *str = luaL_checkstring(L, 1);
+ ptp_verbose_log("%s\n", str);
+ return 1;
+}
+
+static int lua_script_set_status(lua_State *L) {
+ const char *str = luaL_checkstring(L, 1);
+ //ui_send_text("status", (char *)str);
+ return 1;
+}
+
+static int mylua_itoa(lua_State* L) {
+ int number = luaL_checkinteger(L, 1);
+ char buffer[20];
+ snprintf(buffer, sizeof(buffer), "%d", number);
+ lua_pushstring(L, buffer);
+ return 1;
+}
+
+int lua_mark_dead(lua_State *L) {
+ int id = get_task_id(L);
+ if (id == -1) abort();
+ lua_tasks.state[id] = 1;
+ return 1;
+}
+
+lua_State *cam_lua_state(void) {
+ lua_State *L = luaL_newstate();
+ luaopen_base(L);
+ luaL_requiref(L, "json", luaopen_cjson, 1);
+ luaL_requiref(L, "ptp", luaopen_ptp, 1);
+ luaL_requiref(L, "fuji", luaopen_fuji, 1);
+ lua_register(L, "print", lua_script_print);
+ lua_register(L, "exit", lua_mark_dead);
+ lua_register(L, "setStatusText", lua_script_set_status);
+ return L;
+}
+
+int cam_run_lua_script(const char *buffer) {
+ lua_State *L = cam_lua_state();
+ if (L == NULL) {
+ snprintf(error_buffer, sizeof(error_buffer), "Reached max concurrent Lua tasks");
+ return -1;
+ }
+
+ if (luaL_loadbuffer(L, buffer, strlen(buffer), "script") != LUA_OK) {
+ snprintf(error_buffer, sizeof(error_buffer), "Failed to run Lua: %s", lua_tostring(L, -1));
+ lua_close(L);
+ return -1;
+ }
+
+ if (lua_pcall(L, 0, 0, 0) != LUA_OK) {
+ snprintf(error_buffer, sizeof(error_buffer), "Failed to run Lua: %s", lua_tostring(L, -1));
+ lua_close(L);
+ return -1;
+ }
+
+ lua_close(L);
+ return 0;
+}
+
+lua_State *cam_new_task(int *id) {
+ (*id) = -1;
+ if (lua_tasks.tasks >= MAX_LUA_CONCURRENT) {
+ for (int i = 0; i < lua_tasks.tasks; i++) {
+ if (lua_tasks.state[i] == 1) {
+ (*id) = i;
+ lua_close(lua_tasks.L[i]);
+ lua_tasks.L[i] = 0;
+ break;
+ }
+ }
+ if ((*id) == -1) {
+ return NULL;
+ }
+ } else {
+ (*id) = lua_tasks.tasks;
+ lua_tasks.tasks++;
+ }
+
+ lua_State *L = cam_lua_state();
+
+ lua_tasks.L[(*id)] = L;
+ lua_tasks.state[(*id)] = 0;
+
+ return L;
+}
+
+int cam_run_lua_script_async(const char *buffer) {
+ int id = 0;
+ lua_State *L = cam_new_task(&id);
+ if (L == NULL) {
+ snprintf(error_buffer, sizeof(error_buffer), "Reached max concurrent Lua tasks");
+ return -1;
+ }
+
+ if (luaL_loadbuffer(L, buffer, strlen(buffer), "script") != LUA_OK) {
+ snprintf(error_buffer, sizeof(error_buffer), "Failed to run Lua: %s", lua_tostring(L, -1));
+ lua_close(L);
+ return -1;
+ }
+
+ if (lua_pcall(L, 0, 0, 0) != LUA_OK) {
+ snprintf(error_buffer, sizeof(error_buffer), "Failed to run Lua: %s", lua_tostring(L, -1));
+ lua_close(L);
+ return -1;
+ }
+
+ return id;
+}
diff --git a/lib/uilua.c b/lib/uilua.c
index 5d4ab65..7a2c9a8 100644
--- a/lib/uilua.c
+++ b/lib/uilua.c
@@ -1,11 +1,8 @@
-
#include
-
#include
#include
#include
-
-#include "ui.h"
+#include
/*