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 /*