Compare commits

...

8 commits

Author SHA1 Message Date
Lephenixnoir
6d75c3aa82
gdb-bridge: add missing include
Appears to be transitively included on your usual Linux distros but
not on MacOS.
2024-09-23 11:22:28 +02:00
Lephenixnoir
1ceeabdc36
fxconv: recognize the new line-distance param for fonts 2024-09-14 07:29:47 +02:00
Lephenixnoir
92a95e0090
libfxlink: no-colors option + don't always enumerate devices [Windows]
Enumerating devices constantly caused some sort of error/crash that
would stop the monitor after just one frame or so.
2024-09-14 07:29:15 +02:00
Lephenixnoir
efcd6ec241
libfxlink, fxlink: install rules for MinGW build [Windows] 2024-09-14 07:29:10 +02:00
Lephenixnoir
9de441d0f4
libfxlink: basic device enumeration without hotplug [Windows] 2024-09-14 07:26:56 +02:00
Lephenixnoir
c3d7fd6efa
fxlink: find SDL2 without -mwindows to stay in console [Windows]
When loading SDL2 with pkg-config the -mwindows flag is added, which
instructs the loader to load the program through WinMain() and not
create a console for it. This is intended for GUI programs. However,
fxlink is a CLI program with just occasionally an SDL window on top of
it. Disable -mwindows to keep the console and terminal output. I don't
know how to do that with the pkg-config CMake module, so use the SDL2
module for the Windows build instead.
2024-09-14 07:26:56 +02:00
Lephenixnoir
b83796a382
fxgxa: use standard fopen() for reading files [Windows] 2024-09-14 07:26:56 +02:00
Lephenixnoir
50997a8b75
very crude Windows build (WIP): disable fxlink TUI, gdb bridge
There is no direct replacement for poll() in the Windows API, so I'm
gonna disable the fxlink TUI for now and maybe later figure out how to
do something equivalent, even if more brute-forcey.
2024-09-14 07:26:56 +02:00
20 changed files with 255 additions and 73 deletions

1
.gitignore vendored
View file

@ -1,5 +1,6 @@
# Build directory
build/
build-win64/
# Documentation drafts
doc/

View file

@ -13,9 +13,16 @@ if(NOT FXLINK_DISABLE_UDISKS2)
pkg_check_modules(udisks2 REQUIRED udisks2 IMPORTED_TARGET)
endif()
if(NOT FXLINK_DISABLE_SDL2)
if(WIN32)
set(SDL2_NO_MWINDOWS 1)
find_package(SDL2 REQUIRED)
else()
pkg_check_modules(sdl2 REQUIRED sdl2 IMPORTED_TARGET)
set(SDL2_LIBRARIES PkgConfig::sdl2)
set(SDL2_INCLUDE_DIRS)
endif()
pkg_check_modules(ncurses REQUIRED ncurses IMPORTED_TARGET)
endif()
pkg_check_modules(ncurses REQUIRED ncursesw IMPORTED_TARGET)
set(CMAKE_INSTALL_MESSAGE LAZY)
set(SRC "${CMAKE_CURRENT_SOURCE_DIR}")
@ -30,8 +37,13 @@ target_include_directories(fxgxa PUBLIC fxgxa/)
target_link_libraries(fxgxa PkgConfig::libpng)
# fxg1a as a symlink (for compatibility)
if(WIN32)
add_custom_target(fxg1a ALL
COMMAND ${CMAKE_COMMAND} -E create_symlink "fxgxa.exe" "fxg1a.exe")
else()
add_custom_target(fxg1a ALL
COMMAND ${CMAKE_COMMAND} -E create_symlink "fxgxa" "fxg1a")
endif()
# fxsdk
add_custom_command(OUTPUT "${BIN}/fxsdk.sh"
@ -82,17 +94,26 @@ if(NOT FXLINK_DISABLE_UDISKS2)
target_link_libraries(fxlink PkgConfig::udisks2)
endif()
if(NOT FXLINK_DISABLE_SDL2)
target_link_libraries(fxlink PkgConfig::sdl2)
target_link_libraries(fxlink ${SDL2_LIBRARIES})
target_include_directories(fxlink PRIVATE ${SDL2_INCLUDE_DIRS})
endif()
# fxsdk-gdb-bridge
if(NOT WIN32)
add_executable(fxsdk-gdb-bridge fxsdk/gdb-bridge.c)
target_link_libraries(fxsdk-gdb-bridge libfxlink)
target_include_directories(fxsdk-gdb-bridge PRIVATE
"${SRC}/fxlink/include")
endif()
# Install rules
set(SYSROOT "")
if(WIN32)
set(SYSROOT "x86_64-w64-mingw32/")
endif()
if(NOT WIN32)
# fxsdk
install(PROGRAMS "${BIN}/fxsdk.sh" TYPE BIN RENAME fxsdk)
install(DIRECTORY fxsdk/assets DESTINATION share/fxsdk)
@ -105,11 +126,17 @@ install(FILES "${BIN}/fxg1a" TYPE BIN)
# fxconv
install(PROGRAMS fxconv/fxconv-main.py TYPE BIN RENAME fxconv)
install(FILES fxconv/fxconv.py TYPE BIN)
# libfxlink
install(FILES "${BIN}/include/fxlink/config.h" DESTINATION include/fxlink/)
install(DIRECTORY libfxlink/include/ DESTINATION include
FILES_MATCHING PATTERN "*.h")
install(DIRECTORY libfxlink/cmake/ DESTINATION lib/cmake)
install(TARGETS libfxlink DESTINATION lib)
# fxlink
install(TARGETS fxlink)
else()
# fxlink
install(TARGETS fxlink DESTINATION "${SYSROOT}bin")
endif()
# libfxlink
install(FILES "${BIN}/include/fxlink/config.h"
DESTINATION "${SYSROOT}include/fxlink/")
install(DIRECTORY libfxlink/include/ DESTINATION "${SYSROOT}include"
FILES_MATCHING PATTERN "*.h")
install(DIRECTORY libfxlink/cmake/ DESTINATION "${SYSROOT}lib/cmake")
install(TARGETS libfxlink DESTINATION "${SYSROOT}lib")

View file

@ -169,3 +169,24 @@ When configuring, you should set an install prefix that you have write access to
```
You can then proceed to install the cross-compiler. If in doubt about the order in which you need to install repositories, refer to the [GiteaPC README](https://gitea.planet-casio.com/Lephenixnoir/GiteaPC) or check the `giteapc.make` files of each repository, where dependencies are listed.
## Building for Windows from Arch Linux using a cross-compiler
⚠️ This is experimental/not yet officially supported. Intended to build fxlink only.
_Credits to [Cahute](https://cahuteproject.org/guides/build.html) from which this process is based._
Install the cross-compiler and cross-libraries from AUR.
```bash
% yay -S python python-toml mingw-w64 \
mingw-w64-cmake mingw-w64-libusb mingw-w64-sdl2 mingw-w64-libpng \
mingw-w64-ncurses
```
Configure and build. Install to get libfxlink.
```bash
% x86_64-w64-mingw32-cmake -B build-win64 -DCMAKE_INSTALL_PREFIX="$HOME/.local" -DFXLINK_DISABLE_UDISKS2=1 -DFXLINK_DISABLE_POLL=1 -DFXLINK_DISABLE_ANSIESC=1
% make -C build-win64 install
```

View file

@ -755,6 +755,9 @@ def convert_topti(input, params):
# Default character spacing to 1
char_spacing = int(params.get("char-spacing", "1"))
# Default line distance to line height + 1
line_distance = int(params.get("line-distance", line_height + 1))
#--
# Encoding blocks
#---
@ -832,7 +835,7 @@ def convert_topti(input, params):
o += u8(flags) + u8(line_height) + u8(grid.h) + u8(len(blocks))
o += u32(glyph_count)
o += u8(char_spacing) + bytes(3)
o += u8(char_spacing) + u8(line_distance) + bytes(2)
o += ref(data_blocks)
o += ref(data_glyphs)

View file

@ -53,7 +53,7 @@ static int check_g1a(int test, struct g1a const *g1a, size_t size,char *status)
m("Sequence 2 0x01 0x%02x", h->seq2);
return (h->seq2 != 0x01) ? 1:0;
case 5:
m("File size 1 %-8zu %u", size,
m("File size 1 %-8lu %u", (unsigned long)size,
be32toh(h->filesize_be1));
return (be32toh(h->filesize_be1) != size) ? 2:0;
case 6:
@ -66,7 +66,7 @@ static int check_g1a(int test, struct g1a const *g1a, size_t size,char *status)
be16toh(h->checksum));
return (be16toh(h->checksum) != sum) ? 2:0;
case 8:
m("File size 2 %-8zu %u", size,
m("File size 2 %-8lu %u", (unsigned long)size,
be32toh(h->filesize_be2));
return (be32toh(h->filesize_be2) != size) ? 2:0;
default:
@ -170,7 +170,7 @@ static int check_g3a(int test, struct g3a const *g3a, size_t size,char *status)
m("Sequence 2 0x01 0x%02x", h->seq2);
return (h->seq2 != 0x01) ? 1:0;
case 5:
m("File size 1 %-8zu %u", size,
m("File size 1 %-8lu %u", (unsigned long)size,
be32toh(h->filesize_be1));
return (be32toh(h->filesize_be1) != size) ? 2:0;
case 6:
@ -183,7 +183,7 @@ static int check_g3a(int test, struct g3a const *g3a, size_t size,char *status)
be16toh(h->checksum));
return (be16toh(h->checksum) != sum) ? 2:0;
case 8:
m("File size 2 %-8zu %u", size - 0x7004,
m("File size 2 %-8lu %u", (unsigned long)size - 0x7004,
be32toh(h->filesize_be2));
return (be32toh(h->filesize_be2) != size - 0x7004) ? 2:0;
case 9:
@ -192,7 +192,7 @@ static int check_g3a(int test, struct g3a const *g3a, size_t size,char *status)
be32toh(h->checksum_2));
return (be32toh(h->checksum_2) != sum2) ? 2:0;
case 10:
m("File size 1 %-8zu %u", size,
m("File size 1 %-8lu %u", (unsigned long)size,
be32toh(h->filesize_be3));
return (be32toh(h->filesize_be3) != size) ? 2:0;
case 11:

View file

@ -24,6 +24,42 @@
#define be64toh(x) OSSwapBigToHostInt64(x)
#define le64toh(x) OSSwapLittleToHostInt64(x)
#elif defined(__WINDOWS__) || defined(_WIN16) || defined(_WIN32) || defined(_WIN64)
#include <winsock2.h>
#if BYTE_ORDER == LITTLE_ENDIAN
#define htobe16(x) __builtin_bswap16(x)
#define htole16(x) (x)
#define be16toh(x) __builtin_bswap16(x)
#define le16toh(x) (x)
#define htobe32(x) __builtin_bswap32(x)
#define htole32(x) (x)
#define be32toh(x) __builtin_bswap32(x)
#define le32toh(x) (x)
#define htobe64(x) __builtin_bswap64(x)
#define htole64(x) (x)
#define be64toh(x) __builtin_bswap64(x)
#define le64toh(x) (x)
#elif BYTE_ORDER == BIG_ENDIAN
#define htobe16(x) (x)
#define htole16(x) __builtin_bswap16(x)
#define be16toh(x) (x)
#define le16toh(x) __builtin_bswap16(x)
#define htobe32(x) (x)
#define htole32(x) __builtin_bswap32(x)
#define be32toh(x) (x)
#define le32toh(x) __builtin_bswap32(x)
#define htobe64(x) (x)
#define htole64(x) __builtin_bswap64(x)
#define be64toh(x) (x)
#define le64toh(x) __builtin_bswap64(x)
#endif
#elif defined(__linux__)
#include <sys/types.h>

View file

@ -19,7 +19,7 @@ static void invert_header(void *gxa)
#define fail(msg, ...) { \
fprintf(stderr, "error: " msg ": %m\n", ##__VA_ARGS__); \
close(fd); \
fclose(fp); \
free(data); \
return NULL; \
}
@ -28,31 +28,31 @@ static void invert_header(void *gxa)
Allocates a buffer with @prepend leading bytes initialized to zero. */
static void *load(const char *filename, size_t *size, int header, int footer)
{
int fd;
struct stat statbuf;
FILE *fp;
void *data = NULL;
size_t filesize;
long filesize;
fd = open(filename, O_RDONLY);
if(fd < 0) fail("cannot open %s", filename);
fp = fopen(filename, "rb");
if(!fp) fail("cannot open %s", filename);
int x = fstat(fd, &statbuf);
if(x > 0) fail("cannot stat %s", filename);
filesize = statbuf.st_size;
fseek(fp, 0, SEEK_END);
filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if(ftell(fp) != 0) fail("cannod rewind %s", filename);
data = malloc(header + filesize + footer);
if(!data) fail("cannot load %s", filename);
size_t remaining = filesize;
long remaining = filesize;
while(remaining > 0)
{
size_t offset = header + filesize - remaining;
ssize_t y = read(fd, data + offset, remaining);
long offset = header + filesize - remaining;
long y = fread(data + offset, 1, remaining, fp);
if(y < 0) fail("cannot read from %s", filename);
if(y <= 0) fail("cannot read from %s at offset %ld",
filename, offset);
remaining -= y;
}
close(fd);
fclose(fp);
memset(data, 0, header);
memset(data + header + filesize, 0, footer);

View file

@ -16,6 +16,11 @@
#include <errno.h>
#include <locale.h>
#ifndef FXLINK_DISABLE_SDL2
/* Grab main()-related macros */
#include <SDL2/SDL.h>
#endif
static const char *help_string =
"usage: %1$s (-l|-b|-t) [General options]\n"
" %1$s -i [-r] [--fxlink-log[=<FILE>]] [General options]\n"
@ -237,7 +242,9 @@ int main(int argc, char **argv)
while(repeat);
}
else if(mode == 't') {
#ifndef FXLINK_DISABLE_POLL
rc = main_tui_interactive(context);
#endif
}
else if(mode == 'p') {
rc = main_push(filter, &delay, context, argv + optind);

View file

@ -4,6 +4,9 @@
// \___/ License: MIT <https://opensource.org/licenses/MIT> //
//---------------------------------------------------------------------------//
#include <fxlink/config.h>
#ifndef FXLINK_DISABLE_POLL
#include "tui.h"
#include "command-util.h"
#include <string.h>
@ -377,3 +380,5 @@ FXLINK_COMMAND("?cmdtree")
node_dump(cmdtree, 0);
return 0;
}
#endif

View file

@ -4,6 +4,9 @@
// \___/ License: MIT <https://opensource.org/licenses/MIT> //
//---------------------------------------------------------------------------//
#include <fxlink/config.h>
#ifndef FXLINK_DISABLE_POLL
#include "tui.h"
#include "command-util.h"
#include <string.h>
@ -271,3 +274,5 @@ FXLINK_COMMAND("gintctl test all", DEVICE(fdev))
test_read_unaligned(fdev);
return 0;
}
#endif

View file

@ -4,6 +4,9 @@
// \___/ License: MIT <https://opensource.org/licenses/MIT> //
//---------------------------------------------------------------------------//
#include <fxlink/config.h>
#ifndef FXLINK_DISABLE_POLL
#include "../fxlink.h"
#include "tui.h"
#include <fxlink/tooling/libpng.h>
@ -202,24 +205,24 @@ static void TUI_render_transfers(void)
if(!comm)
continue;
struct fxlink_transfer *IN = comm->ftransfer_IN;
struct fxlink_transfer *OUT = comm->ftransfer_OUT;
struct fxlink_transfer *in = comm->ftransfer_IN;
struct fxlink_transfer *out = comm->ftransfer_OUT;
if(IN) {
if(in) {
mvwaddstr(win, y, 1, fxlink_device_id(fdev));
mvwaddstr(win, y, 10, "IN");
mvwaddstr(win, y, 16, fxlink_size_string(IN->msg.size));
mvwaddstr(win, y, 16, fxlink_size_string(in->msg.size));
wmove(win, y, 26);
progress_bar(win, 32, IN->processed_size, IN->msg.size);
progress_bar(win, 32, in->processed_size, in->msg.size);
has_transfers = true;
y++;
}
if(OUT) {
if(out) {
mvwaddstr(win, y, 1, fxlink_device_id(fdev));
mvwaddstr(win, y, 10, "OUT");
mvwaddstr(win, y, 16, fxlink_size_string(OUT->msg.size));
mvwaddstr(win, y, 16, fxlink_size_string(out->msg.size));
has_transfers = true;
y++;
}
@ -259,10 +262,12 @@ static bool TUI_setup(void)
memset(&TUI, 0, sizeof TUI);
/* Set up the SINGWINCH handler */
#if !defined(_WIN16) && !defined(_WIN32) && !defined(_WIN64)
struct sigaction WINCH;
sigaction(SIGWINCH, NULL, &WINCH);
WINCH.sa_handler = TUI_SIGWINCH_handler;
sigaction(SIGWINCH, &WINCH, NULL);
#endif
/* Initialize the main screen */
initscr();
@ -590,3 +595,5 @@ int main_tui_interactive(libusb_context *ctx)
TUI_quit();
return 0;
}
#endif

View file

@ -1,6 +1,7 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>

View file

@ -1,22 +1,37 @@
# Locate the library file and includes
if(WIN32)
set(SYSROOT "x86_64-w64-mingw32/")
else()
set(SYSROOT)
endif()
find_library(
LIBFXLINK_PATH "fxlink"
HINTS "$ENV{HOME}/.local/lib" "$ENV{FXSDK_PATH}/lib"
LIBFXLINK_PATH "fxlink" "libfxlink.a"
HINTS "$ENV{HOME}/.local/${SYSROOT}lib" "$ENV{FXSDK_PATH}/${SYSROOT}lib"
)
if(LIBFXLINK_PATH STREQUAL "LIBFXLINK_PATH-NOTFOUND")
message(SEND_ERROR
# Try the paths directly
set(P1 "$ENV{HOME}/.local/${SYSROOT}lib/libfxlink.a")
set(P2 "$ENV{FXSDK_PATH}/${SYSROOT}lib/libfxlink.a")
if(EXISTS "${P1}")
set(LIBFXLINK_PATH "${P1}")
elseif(DEFINED "$ENV{FXSDK_PATH}" AND EXISTS "${P2}")
set(LIBFXLINK_PATH "${P2}")
else()
message(FATAL_ERROR
"Could not find libfxlink.a!\n"
"You can specify the install path with the environment variable "
"FXSDK_PATH, such as FXSDK_PATH=$HOME/.local")
else()
endif()
endif()
get_filename_component(LIBFXLINK_PATH "${LIBFXLINK_PATH}/../.." ABSOLUTE)
set(LIBFXLINK_LIB "${LIBFXLINK_PATH}/lib/libfxlink.a")
set(LIBFXLINK_INCLUDE "${LIBFXLINK_PATH}/include")
message("(libfxlink) Found libfxlink at: ${LIBFXLINK_LIB}")
message("(libfxlink) Will take includes from: ${LIBFXLINK_INCLUDE}")
endif()
# Find library version

View file

@ -15,6 +15,9 @@
char const *fmt_to_ANSI(int format)
{
#ifdef FXLINK_DISABLE_ANSIESC
return "";
#else
static char buf[64];
int n = 0;
@ -36,6 +39,7 @@ char const *fmt_to_ANSI(int format)
strcpy(buf+n, "\e[3m"), n += 4;
return buf;
#endif
}
char *fxlink_gen_file_name(char const *path, char const *name,
@ -47,7 +51,11 @@ char *fxlink_gen_file_name(char const *path, char const *name,
time_t time_raw;
struct tm time_bd;
time(&time_raw);
#if defined(_WIN16) || defined(_WIN32) || defined(_WIN64)
time_bd = *localtime(&time_raw);
#else
localtime_r(&time_raw, &time_bd);
#endif
while(1) {
asprintf(&filename, "%s/fxlink-%.16s-%04d.%02d.%02d-%02dh%02d-%d%s",
@ -67,6 +75,7 @@ char *fxlink_gen_file_name(char const *path, char const *name,
return filename;
}
#ifndef FXLINK_DISABLE_POLL
int fxlink_multipoll(int timeout, struct pollfd *fds1, int count1, ...)
{
/* Convenience macro to iterate on file descriptor arrays */
@ -104,6 +113,7 @@ int fxlink_multipoll(int timeout, struct pollfd *fds1, int count1, ...)
free(concat);
return rc;
}
#endif /* FXLINK_DISABLE_POLL */
char const *fxlink_size_string(int bytes)
{

View file

@ -227,7 +227,8 @@ void fxlink_device_analysis_1(struct fxlink_device *fdev, bool quiet)
if(!quiet) {
hlog("calculators %s", fxlink_device_id(fdev));
log_("%1$d interface%2$s, class code%2$s", calc->interface_count,
log_("%d interface%s, class code%s", calc->interface_count,
calc->interface_count != 1 ? "s" : "",
calc->interface_count != 1 ? "s" : "");
for(int i = 0; i < calc->interface_count; i++) {
@ -635,6 +636,8 @@ bool fxlink_device_start_bulk_OUT(struct fxlink_device *fdev,
// Polled file descriptor tracking
//---
#ifndef FXLINK_DISABLE_POLL
static void generate_poll_fds(struct fxlink_pollfds *tracker)
{
/* Get the set of libusb file descriptors to poll for news */
@ -705,6 +708,8 @@ void fxlink_pollfds_stop(struct fxlink_pollfds *tracker)
memset(tracker, 0, sizeof *tracker);
}
#endif /* FXLINK_DISABLE_POLL */
//---
// Device tracking
//---
@ -813,13 +818,16 @@ bool fxlink_device_list_track(struct fxlink_device_list *list,
libusb_context *ctx)
{
memset(list, 0, sizeof *list);
list->ctx = ctx;
if(!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
elog("libusb doesn't handle hotplug; devices may not be detected\n");
list->hotplug_supported = false;
enumerate_devices(ctx, list);
return false;
}
list->ctx = ctx;
list->hotplug_supported = true;
libusb_hotplug_register_callback(ctx,
/* Both arriving and departing devices */
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,

View file

@ -29,7 +29,13 @@ static char *read_word(char const **input)
{
char const *str = *input;
while(**input && isword(**input)) (*input)++;
return strndup(str, *input - str);
char *rc = malloc(*input - str + 1);
if(!rc)
return NULL;
memcpy(rc, str, *input - str);
rc[*input - str] = 0;
return rc;
}
/* Reads a property from the input source. Advances *input and sets *name and

View file

@ -13,5 +13,11 @@
/* Disable SDL2 interfaces. */
#cmakedefine FXLINK_DISABLE_SDL2
/* Disable poll-based interfaces. */
#cmakedefine FXLINK_DISABLE_POLL
/* Disable printing ANSI escape sequences. */
#cmakedefine FXLINK_DISABLE_ANSIESC
/* fxSDK version */
#define FXLINK_VERSION "@CMAKE_PROJECT_VERSION@"

View file

@ -12,7 +12,18 @@
#include <stdint.h>
#include <stdarg.h>
#include <stdio.h>
#ifndef FXLINK_DISABLE_POLL
# include <poll.h>
#endif
#ifdef min
# undef min
#endif
#ifdef max
# undef max
#endif
static inline int min(int x, int y)
{
@ -84,8 +95,10 @@ char const *fmt_to_ANSI(int format);
char *fxlink_gen_file_name(char const *path, char const *name,
char const *suffix);
#ifndef FXLINK_DISABLE_POLL
/* Modified poll with a variable number of arrays. */
int fxlink_multipoll(int timeout, struct pollfd *fds1, int count1, ...);
#endif
/* Write out the given size (in bytes) in a human-readable form. Returns a
pointer to a statically-allocated string. */

View file

@ -48,8 +48,8 @@
#pragma once
#include <fxlink/protocol.h>
#include <fxlink/filter.h>
#include <fxlink/defs.h>
#include <libusb.h>
#include <poll.h>
/* Device information tracked for every USB device. */
struct fxlink_device {
@ -255,6 +255,8 @@ void fxlink_device_cleanup(struct fxlink_device *fdev);
// notification API.
//---
#ifndef FXLINK_DISABLE_POLL
/* Tracker for libusb file descriptors to be polled in a main loop. */
struct fxlink_pollfds {
/* libusb context to be tracked (must remain constant) */
@ -274,6 +276,8 @@ void fxlink_pollfds_track(struct fxlink_pollfds *tracker, libusb_context *ctx);
called before the tracker structure gets destroyed. */
void fxlink_pollfds_stop(struct fxlink_pollfds *tracker);
#endif /* FXLINK_DISABLE_POLL */
//---
// Device tracking
//
@ -295,6 +299,9 @@ struct fxlink_device_list {
libusb_context *ctx;
/* Callback handle */
libusb_hotplug_callback_handle hotplug_handle;
/* Whether the hotplug callback could be installed. If not, refreshes
will be made manually. */
bool hotplug_supported;
/* Array of connected devices */
struct fxlink_device *devices;
/* Number of elements in `devices` */

View file

@ -84,7 +84,11 @@ int hlog(char const *fmt, ...)
{
time_t t = time(NULL);
struct tm tm;
#if defined(_WIN16) || defined(_WIN32) || defined(_WIN64)
tm = *localtime(&t);
#else
localtime_r(&t, &tm);
#endif
flog(FMT_WHITE | FMT_DIM, "[%02d:%02d] ", tm.tm_hour, tm.tm_min);
LOG_VA_ARGS(fmt, flogv(FMT_YELLOW | FMT_DIM, fmt, _args));