aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2019-08-03 18:42:39 +0200
committercbdev <cb@cbcdn.com>2019-08-03 18:42:39 +0200
commit20a6882a063404858588596bd3f12bdd9e53460a (patch)
treec13ee23aec08ac3c29a1ff9efe2e429d87041239
parentbbeade8898200a8024169ece30c620016fd5eaf1 (diff)
downloadmidimonster-20a6882a063404858588596bd3f12bdd9e53460a.tar.gz
midimonster-20a6882a063404858588596bd3f12bdd9e53460a.tar.bz2
midimonster-20a6882a063404858588596bd3f12bdd9e53460a.zip
Windows build compatiblity
-rw-r--r--.gitignore3
-rw-r--r--Makefile23
-rw-r--r--backend.c31
-rw-r--r--backend.h9
-rw-r--r--backends/Makefile26
-rw-r--r--backends/artnet.c16
-rw-r--r--backends/artnet.h2
-rw-r--r--backends/libmmbackend.c12
-rw-r--r--backends/libmmbackend.h6
-rw-r--r--backends/lua.c4
-rw-r--r--backends/osc.c18
-rw-r--r--backends/osc.h2
-rw-r--r--backends/sacn.c28
-rw-r--r--backends/sacn.h1
-rw-r--r--config.c56
-rw-r--r--midimonster.c39
-rw-r--r--midimonster.h38
-rw-r--r--monster.cfg12
-rw-r--r--plugin.c60
-rw-r--r--portability.h17
20 files changed, 326 insertions, 77 deletions
diff --git a/.gitignore b/.gitignore
index 3afd872..4396a38 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
midimonster
+midimonster.exe
+libmmapi.a
*.swp
*.o
*.so
+*.dll
diff --git a/Makefile b/Makefile
index 5ee9cd9..b82d6d8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,14 @@
-.PHONY: all clean run sanitize backends full backends-full
+.PHONY: all clean run sanitize backends windows full backends-full
OBJS = config.o backend.o plugin.o
PLUGINDIR = "\"./backends/\""
+PLUGINDIR_W32 = "\"backends\\\\\""
SYSTEM := $(shell uname -s)
-CFLAGS ?= -g -Wall
+CFLAGS ?= -g -Wall -Wpedantic
+# Hide all non-API symbols for export
+CFLAGS += -fvisibility=hidden
+
#CFLAGS += -DDEBUG
midimonster: LDLIBS = -ldl
midimonster: CFLAGS += -DPLUGINS=$(PLUGINDIR)
@@ -21,6 +25,8 @@ all: midimonster backends
full: midimonster backends-full
+windows: midimonster.exe
+
backends:
$(MAKE) -C backends
@@ -31,8 +37,21 @@ backends-full:
midimonster: midimonster.c portability.h $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $< $(OBJS) $(LDLIBS) -o $@
+midimonster.exe: export CC = x86_64-w64-mingw32-gcc
+#midimonster.exe: CFLAGS += -Wno-format
+midimonster.exe: CFLAGS += -DPLUGINS=$(PLUGINDIR_W32) -Wno-format
+midimonster.exe: LDLIBS = -lws2_32
+midimonster.exe: LDFLAGS += -Wl,--out-implib,libmmapi.a
+midimonster.exe: midimonster.c portability.h $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $< $(OBJS) $(LDLIBS) -o $@
+ # The windows build for backends requires the import library generated with the build,
+ # so the backends can't be a prerequisite for the executable...
+ $(MAKE) -C backends windows
+
clean:
$(RM) midimonster
+ $(RM) midimonster.exe
+ $(RM) libmmapi.a
$(RM) $(OBJS)
$(MAKE) -C backends clean
diff --git a/backend.c b/backend.c
index 5df5d73..4fa7704 100644
--- a/backend.c
+++ b/backend.c
@@ -1,4 +1,9 @@
#include <string.h>
+#ifndef _WIN32
+#define MM_API __attribute__((visibility ("default")))
+#else
+#define MM_API __attribute__((dllexport))
+#endif
#include "midimonster.h"
#include "backend.h"
@@ -26,7 +31,7 @@ int backends_handle(size_t nfds, managed_fd* fds){
}
}
- DBGPF("Notifying backend %s of %zu waiting FDs\n", backends[u].name, n);
+ DBGPF("Notifying backend %s of %lu waiting FDs\n", backends[u].name, n);
rv |= backends[u].process(n, fds);
if(rv){
fprintf(stderr, "Backend %s failed to handle input\n", backends[u].name);
@@ -59,28 +64,28 @@ int backends_notify(size_t nev, channel** c, channel_value* v){
}
}
- DBGPF("Calling handler for instance %s with %zu events\n", instances[u]->name, n);
+ DBGPF("Calling handler for instance %s with %lu events\n", instances[u]->name, n);
rv |= instances[u]->backend->handle(instances[u], n, c, v);
}
return 0;
}
-channel* mm_channel(instance* i, uint64_t ident, uint8_t create){
+channel* MM_API mm_channel(instance* inst, uint64_t ident, uint8_t create){
size_t u;
for(u = 0; u < nchannels; u++){
- if(channels[u]->instance == i && channels[u]->ident == ident){
- DBGPF("Requested channel %zu on instance %s already exists, reusing\n", ident, i->name);
+ if(channels[u]->instance == inst && channels[u]->ident == ident){
+ DBGPF("Requested channel %lu on instance %s already exists, reusing\n", ident, inst->name);
return channels[u];
}
}
if(!create){
- DBGPF("Requested unknown channel %zu on instance %s\n", ident, i->name);
+ DBGPF("Requested unknown channel %lu on instance %s\n", ident, inst->name);
return NULL;
}
- DBGPF("Creating previously unknown channel %zu on instance %s\n", ident, i->name);
+ DBGPF("Creating previously unknown channel %lu on instance %s\n", ident, inst->name);
channel** new_chan = realloc(channels, (nchannels + 1) * sizeof(channel*));
if(!new_chan){
fprintf(stderr, "Failed to allocate memory\n");
@@ -95,12 +100,12 @@ channel* mm_channel(instance* i, uint64_t ident, uint8_t create){
return NULL;
}
- channels[nchannels]->instance = i;
+ channels[nchannels]->instance = inst;
channels[nchannels]->ident = ident;
return channels[nchannels++];
}
-instance* mm_instance(){
+instance* MM_API mm_instance(){
instance** new_inst = realloc(instances, (ninstances + 1) * sizeof(instance*));
if(!new_inst){
//TODO free
@@ -118,7 +123,7 @@ instance* mm_instance(){
return instances[ninstances++];
}
-instance* mm_instance_find(char* name, uint64_t ident){
+instance* MM_API mm_instance_find(char* name, uint64_t ident){
size_t u;
backend* b = backend_match(name);
if(!b){
@@ -134,7 +139,7 @@ instance* mm_instance_find(char* name, uint64_t ident){
return NULL;
}
-int mm_backend_instances(char* name, size_t* ninst, instance*** inst){
+int MM_API mm_backend_instances(char* name, size_t* ninst, instance*** inst){
backend* b = backend_match(name);
size_t n = 0, u;
//count number of affected instances
@@ -177,7 +182,7 @@ void instances_free(){
void channels_free(){
size_t u;
for(u = 0; u < nchannels; u++){
- DBGPF("Destroying channel %zu on instance %s\n", channels[u]->ident, channels[u]->instance->name);
+ DBGPF("Destroying channel %lu on instance %s\n", channels[u]->ident, channels[u]->instance->name);
if(channels[u]->impl){
channels[u]->instance->backend->channel_free(channels[u]);
}
@@ -232,7 +237,7 @@ struct timeval backend_timeout(){
return tv;
}
-int mm_backend_register(backend b){
+int MM_API mm_backend_register(backend b){
if(!backend_match(b.name)){
backends = realloc(backends, (nbackends + 1) * sizeof(backend));
if(!backends){
diff --git a/backend.h b/backend.h
index daf96bc..7529154 100644
--- a/backend.h
+++ b/backend.h
@@ -1,8 +1,8 @@
#include <sys/types.h>
+/* Internal API */
int backends_handle(size_t nfds, managed_fd* fds);
int backends_notify(size_t nev, channel** c, channel_value* v);
-
backend* backend_match(char* name);
instance* instance_match(char* name);
struct timeval backend_timeout();
@@ -10,3 +10,10 @@ int backends_start();
int backends_stop();
void instances_free();
void channels_free();
+
+/* Backend API */
+channel* MM_API mm_channel(instance* inst, uint64_t ident, uint8_t create);
+instance* MM_API mm_instance();
+instance* MM_API mm_instance_find(char* name, uint64_t ident);
+int MM_API mm_backend_instances(char* name, size_t* ninst, instance*** inst);
+int MM_API mm_backend_register(backend b);
diff --git a/backends/Makefile b/backends/Makefile
index 22cb95b..2374df0 100644
--- a/backends/Makefile
+++ b/backends/Makefile
@@ -1,12 +1,13 @@
.PHONY: all clean full
OPTIONAL_BACKENDS = ola.so
+WINDOWS_BACKENDS = loopback.dll artnet.dll osc.dll sacn.dll
LINUX_BACKENDS = midi.so evdev.so
BACKENDS = artnet.so osc.so loopback.so sacn.so lua.so
BACKEND_LIB = libmmbackend.o
SYSTEM := $(shell uname -s)
-CFLAGS += -g -fPIC -I../
+CFLAGS += -g -fPIC -I../ -Wall -Wpedantic
CPPFLAGS += -g -fPIC -I../
LDFLAGS += -shared
@@ -20,8 +21,17 @@ LDFLAGS += -undefined dynamic_lookup
endif
artnet.so: ADDITIONAL_OBJS += $(BACKEND_LIB)
+artnet.dll: ADDITIONAL_OBJS += $(BACKEND_LIB)
+artnet.dll: LDLIBS += -lws2_32
+
osc.so: ADDITIONAL_OBJS += $(BACKEND_LIB)
+osc.dll: ADDITIONAL_OBJS += $(BACKEND_LIB)
+osc.dll: LDLIBS += -lws2_32
+
sacn.so: ADDITIONAL_OBJS += $(BACKEND_LIB)
+sacn.dll: ADDITIONAL_OBJS += $(BACKEND_LIB)
+sacn.dll: LDLIBS += -lws2_32
+
midi.so: LDLIBS = -lasound
evdev.so: CFLAGS += $(shell pkg-config --cflags libevdev)
evdev.so: LDLIBS = $(shell pkg-config --libs libevdev)
@@ -33,12 +43,24 @@ lua.so: LDLIBS += $(shell pkg-config --libs lua5.3)
%.so :: %.c %.h $(BACKEND_LIB)
$(CC) $(CFLAGS) $(LDLIBS) $< $(ADDITIONAL_OBJS) -o $@ $(LDFLAGS)
+%.dll :: %.c %.h $(BACKEND_LIB)
+ $(CC) $(CFLAGS) $< $(ADDITIONAL_OBJS) -o $@ $(LDFLAGS) $(LDLIBS)
+
%.so :: %.cpp %.h
$(CXX) $(CPPFLAGS) $(LDLIBS) $< $(ADDITIONAL_OBJS) -o $@ $(LDFLAGS)
all: $(BACKEND_LIB) $(BACKENDS)
+../libmmapi.a:
+ $(MAKE) -C ../ midimonster.exe
+
+windows: export CC = x86_64-w64-mingw32-gcc
+windows: LDLIBS += -lmmapi
+windows: LDFLAGS += -L../
+windows: CFLAGS += -Wno-format -Wno-pointer-sign
+windows: ../libmmapi.a $(BACKEND_LIB) $(WINDOWS_BACKENDS)
+
full: $(BACKEND_LIB) $(BACKENDS) $(OPTIONAL_BACKENDS)
clean:
- $(RM) $(BACKEND_LIB) $(BACKENDS) $(OPTIONAL_BACKENDS)
+ $(RM) $(BACKEND_LIB) $(BACKENDS) $(OPTIONAL_BACKENDS) $(WINDOWS_BACKENDS)
diff --git a/backends/artnet.c b/backends/artnet.c
index 8b404a6..a6df4ab 100644
--- a/backends/artnet.c
+++ b/backends/artnet.c
@@ -3,8 +3,8 @@
#include <errno.h>
#include "libmmbackend.h"
-
#include "artnet.h"
+
#define MAX_FDS 255
#define BACKEND_NAME "artnet"
@@ -32,7 +32,7 @@ static int artnet_listener(char* host, char* port){
return -1;
}
- fprintf(stderr, "ArtNet backend interface %zu bound to %s port %s\n", artnet_fds, host, port);
+ fprintf(stderr, "ArtNet backend interface %lu bound to %s port %s\n", artnet_fds, host, port);
artnet_fd[artnet_fds].fd = fd;
artnet_fd[artnet_fds].output_instances = 0;
artnet_fd[artnet_fds].output_instance = NULL;
@@ -212,7 +212,7 @@ static int artnet_transmit(instance* inst){
};
memcpy(frame.data, data->data.out, 512);
- if(sendto(artnet_fd[data->fd_index].fd, &frame, sizeof(frame), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
+ if(sendto(artnet_fd[data->fd_index].fd, (uint8_t*) &frame, sizeof(frame), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
fprintf(stderr, "Failed to output ArtNet frame for instance %s: %s\n", inst->name, strerror(errno));
}
@@ -230,7 +230,7 @@ static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v)
artnet_instance_data* data = (artnet_instance_data*) inst->impl;
if(!data->dest_len){
- fprintf(stderr, "ArtNet instance %s not enabled for output (%zu channel events)\n", inst->name, num);
+ fprintf(stderr, "ArtNet instance %s not enabled for output (%lu channel events)\n", inst->name, num);
return 0;
}
@@ -295,7 +295,7 @@ static inline int artnet_process_frame(instance* inst, artnet_pkt* frame){
}
if(!chan){
- fprintf(stderr, "Active channel %zu on %s not known to core\n", p, inst->name);
+ fprintf(stderr, "Active channel %lu on %s not known to core\n", p, inst->name);
return 1;
}
@@ -367,7 +367,11 @@ static int artnet_handle(size_t num, managed_fd* fds){
}
} while(bytes_read > 0);
+ #ifdef _WIN32
+ if(bytes_read < 0 && WSAGetLastError() != WSAEWOULDBLOCK){
+ #else
if(bytes_read < 0 && errno != EAGAIN){
+ #endif
fprintf(stderr, "ArtNet failed to receive data: %s\n", strerror(errno));
}
@@ -438,7 +442,7 @@ static int artnet_start(){
}
}
- fprintf(stderr, "ArtNet backend registering %zu descriptors to core\n", artnet_fds);
+ fprintf(stderr, "ArtNet backend registering %lu descriptors to core\n", artnet_fds);
for(u = 0; u < artnet_fds; u++){
if(mm_manage_fd(artnet_fd[u].fd, BACKEND_NAME, 1, (void*) u)){
goto bail;
diff --git a/backends/artnet.h b/backends/artnet.h
index 90aedd5..f5aa745 100644
--- a/backends/artnet.h
+++ b/backends/artnet.h
@@ -1,4 +1,6 @@
+#ifndef _WIN32
#include <sys/socket.h>
+#endif
#include "midimonster.h"
int init();
diff --git a/backends/libmmbackend.c b/backends/libmmbackend.c
index 6320611..b27ebc5 100644
--- a/backends/libmmbackend.c
+++ b/backends/libmmbackend.c
@@ -53,7 +53,7 @@ int mmbackend_parse_sockaddr(char* host, char* port, struct sockaddr_storage* ad
}
int mmbackend_socket(char* host, char* port, int socktype, uint8_t listener){
- int fd = -1, status, yes = 1, flags;
+ int fd = -1, status, yes = 1;
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = socktype,
@@ -106,12 +106,20 @@ int mmbackend_socket(char* host, char* port, int socktype, uint8_t listener){
}
//set nonblocking
- flags = fcntl(fd, F_GETFL, 0);
+ #ifdef _WIN32
+ u_long mode = 1;
+ if(ioctlsocket(fd, FIONBIO, &mode) != NO_ERROR){
+ closesocket(fd);
+ return 1;
+ }
+ #else
+ int flags = fcntl(fd, F_GETFL, 0);
if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0){
fprintf(stderr, "Failed to set socket nonblocking\n");
close(fd);
return -1;
}
+ #endif
return fd;
}
diff --git a/backends/libmmbackend.h b/backends/libmmbackend.h
index 38bfca0..77cad6a 100644
--- a/backends/libmmbackend.h
+++ b/backends/libmmbackend.h
@@ -1,14 +1,20 @@
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+//#define close closesocket
+#else
#include <sys/socket.h>
#include <netdb.h>
+#endif
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
+#include "../portability.h"
/* Parse spec as host specification in the form
* host port
diff --git a/backends/lua.c b/backends/lua.c
index 4a910a2..ec02575 100644
--- a/backends/lua.c
+++ b/backends/lua.c
@@ -161,8 +161,6 @@ static int lua_callback_output(lua_State* interpreter){
static int lua_callback_interval(lua_State* interpreter){
size_t n = 0;
- instance* inst = NULL;
- lua_instance_data* data = NULL;
uint64_t interval = 0;
int reference = LUA_NOREF;
@@ -174,8 +172,6 @@ static int lua_callback_interval(lua_State* interpreter){
//get instance pointer from registry
lua_pushstring(interpreter, LUA_REGISTRY_KEY);
lua_gettable(interpreter, LUA_REGISTRYINDEX);
- inst = (instance*) lua_touserdata(interpreter, -1);
- data = (lua_instance_data*) inst->impl;
//fetch and round the interval
interval = luaL_checkinteger(interpreter, 2);
diff --git a/backends/osc.c b/backends/osc.c
index 18c8bad..03e431f 100644
--- a/backends/osc.c
+++ b/backends/osc.c
@@ -1,8 +1,8 @@
#include <string.h>
#include <ctype.h>
#include <errno.h>
-#include "libmmbackend.h"
+#include "libmmbackend.h"
#include "osc.h"
/*
@@ -480,14 +480,14 @@ static int osc_register_pattern(osc_instance_data* data, char* pattern_path, cha
//parse min/max values
token = strtok(NULL, " ");
if(!token){
- fprintf(stderr, "Missing minimum specification for parameter %zu of OSC pattern %s\n", u, pattern_path);
+ fprintf(stderr, "Missing minimum specification for parameter %lu of OSC pattern %s\n", u, pattern_path);
return 1;
}
data->pattern[pattern].min[u] = osc_parse_value_spec(format[u], token);
token = strtok(NULL, " ");
if(!token){
- fprintf(stderr, "Missing maximum specification for parameter %zu of OSC pattern %s\n", u, pattern_path);
+ fprintf(stderr, "Missing maximum specification for parameter %lu of OSC pattern %s\n", u, pattern_path);
return 1;
}
data->pattern[pattern].max[u] = osc_parse_value_spec(format[u], token);
@@ -689,7 +689,7 @@ static int osc_output_channel(instance* inst, size_t channel){
//write data
if(offset + osc_data_length(data->channel[channel].type[p]) >= sizeof(xmit_buf)){
- fprintf(stderr, "Insufficient buffer size for OSC transmitting channel %s.%s at parameter %zu\n", inst->name, data->channel[channel].path, p);
+ fprintf(stderr, "Insufficient buffer size for OSC transmitting channel %s.%s at parameter %lu\n", inst->name, data->channel[channel].path, p);
return 1;
}
@@ -720,7 +720,7 @@ static int osc_set(instance* inst, size_t num, channel** c, channel_value* v){
osc_instance_data* data = (osc_instance_data*) inst->impl;
if(!data->dest_len){
- fprintf(stderr, "OSC instance %s does not have a destination, output is disabled (%zu channels)\n", inst->name, num);
+ fprintf(stderr, "OSC instance %s does not have a destination, output is disabled (%lu channels)\n", inst->name, num);
return 0;
}
@@ -778,7 +778,7 @@ static int osc_process_packet(instance* inst, char* local_path, char* format, ui
channel* chan = NULL;
if(payload_len % 4){
- fprintf(stderr, "Invalid OSC packet, data length %zu\n", payload_len);
+ fprintf(stderr, "Invalid OSC packet, data length %lu\n", payload_len);
return 0;
}
@@ -877,7 +877,11 @@ static int osc_handle(size_t num, managed_fd* fds){
}
} while(bytes_read > 0);
+ #ifdef _WIN32
+ if(bytes_read < 0 && WSAGetLastError() != WSAEWOULDBLOCK){
+ #else
if(bytes_read < 0 && errno != EAGAIN){
+ #endif
fprintf(stderr, "OSC failed to receive data for instance %s: %s\n", inst->name, strerror(errno));
}
@@ -924,7 +928,7 @@ static int osc_start(){
}
}
- fprintf(stderr, "OSC backend registered %zu descriptors to core\n", fds);
+ fprintf(stderr, "OSC backend registered %lu descriptors to core\n", fds);
free(inst);
return 0;
diff --git a/backends/osc.h b/backends/osc.h
index 4e9dec5..b2aaea7 100644
--- a/backends/osc.h
+++ b/backends/osc.h
@@ -1,6 +1,8 @@
#include "midimonster.h"
#include <sys/types.h>
+#ifndef _WIN32
#include <sys/socket.h>
+#endif
#define OSC_RECV_BUF 8192
#define OSC_XMIT_BUF 8192
diff --git a/backends/sacn.c b/backends/sacn.c
index 75bb76f..6f7d1a5 100644
--- a/backends/sacn.c
+++ b/backends/sacn.c
@@ -1,16 +1,18 @@
#include <string.h>
#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
+#ifndef _WIN32
+#include <netdb.h>
#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
#include "libmmbackend.h"
-
#include "sacn.h"
+
//upper limit imposed by using the fd index as 16-bit part of the instance id
#define MAX_FDS 4096
#define BACKEND_NAME "sacn"
@@ -71,7 +73,7 @@ static int sacn_listener(char* host, char* port, uint8_t fd_flags){
return -1;
}
- fprintf(stderr, "sACN backend interface %zu bound to %s port %s\n", global_cfg.fds, host, port);
+ fprintf(stderr, "sACN backend interface %lu bound to %s port %s\n", global_cfg.fds, host, port);
global_cfg.fd[global_cfg.fds].fd = fd;
global_cfg.fd[global_cfg.fds].flags = fd_flags;
global_cfg.fd[global_cfg.fds].universes = 0;
@@ -271,7 +273,7 @@ static int sacn_transmit(instance* inst){
memcpy(pdu.data.source_name, global_cfg.source_name, sizeof(pdu.data.source_name));
memcpy((((uint8_t*)pdu.data.data) + 1), data->data.out, 512);
- if(sendto(global_cfg.fd[data->fd_index].fd, &pdu, sizeof(pdu), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
+ if(sendto(global_cfg.fd[data->fd_index].fd, (uint8_t*) &pdu, sizeof(pdu), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
fprintf(stderr, "Failed to output sACN frame for instance %s: %s\n", inst->name, strerror(errno));
}
@@ -293,7 +295,7 @@ static int sacn_set(instance* inst, size_t num, channel** c, channel_value* v){
}
if(!data->xmit_prio){
- fprintf(stderr, "sACN instance %s not enabled for output (%zu channel events)\n", inst->name, num);
+ fprintf(stderr, "sACN instance %s not enabled for output (%lu channel events)\n", inst->name, num);
return 0;
}
@@ -378,7 +380,7 @@ static int sacn_process_frame(instance* inst, sacn_frame_root* frame, sacn_frame
}
if(!chan){
- fprintf(stderr, "Active channel %zu on %s not known to core", u, inst->name);
+ fprintf(stderr, "Active channel %lu on %s not known to core", u, inst->name);
return 1;
}
@@ -445,8 +447,8 @@ static void sacn_discovery(size_t fd){
pdu.data.page = page;
memcpy(pdu.data.data, global_cfg.fd[fd].universe + page * 512, universes * sizeof(uint16_t));
- if(sendto(global_cfg.fd[fd].fd, &pdu, sizeof(pdu) - (512 - universes) * sizeof(uint16_t), 0, (struct sockaddr*) &discovery_dest, sizeof(discovery_dest)) < 0){
- fprintf(stderr, "Failed to output sACN universe discovery frame for interface %zu: %s\n", fd, strerror(errno));
+ if(sendto(global_cfg.fd[fd].fd, (uint8_t*) &pdu, sizeof(pdu) - (512 - universes) * sizeof(uint16_t), 0, (struct sockaddr*) &discovery_dest, sizeof(discovery_dest)) < 0){
+ fprintf(stderr, "Failed to output sACN universe discovery frame for interface %lu: %s\n", fd, strerror(errno));
}
}
}
@@ -512,7 +514,11 @@ static int sacn_handle(size_t num, managed_fd* fds){
}
} while(bytes_read > 0);
+ #ifdef _WIN32
+ if(bytes_read < 0 && WSAGetLastError() != WSAEWOULDBLOCK){
+ #else
if(bytes_read < 0 && errno != EAGAIN){
+ #endif
fprintf(stderr, "sACN failed to receive data: %s\n", strerror(errno));
}
@@ -577,7 +583,7 @@ static int sacn_start(){
if(!data->unicast_input){
mcast_req.imr_multiaddr.s_addr = htobe32(((uint32_t) 0xefff0000) | ((uint32_t) data->uni));
- if(setsockopt(global_cfg.fd[data->fd_index].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcast_req, sizeof(mcast_req))){
+ if(setsockopt(global_cfg.fd[data->fd_index].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (uint8_t*) &mcast_req, sizeof(mcast_req))){
fprintf(stderr, "Failed to join Multicast group for sACN universe %u on instance %s: %s\n", data->uni, inst[u]->name, strerror(errno));
}
}
@@ -604,7 +610,7 @@ static int sacn_start(){
}
}
- fprintf(stderr, "sACN backend registering %zu descriptors to core\n", global_cfg.fds);
+ fprintf(stderr, "sACN backend registering %lu descriptors to core\n", global_cfg.fds);
for(u = 0; u < global_cfg.fds; u++){
//allocate memory for storing last frame transmission timestamp
global_cfg.fd[u].last_frame = calloc(global_cfg.fd[u].universes, sizeof(uint64_t));
diff --git a/backends/sacn.h b/backends/sacn.h
index e7106f7..7af2a36 100644
--- a/backends/sacn.h
+++ b/backends/sacn.h
@@ -1,4 +1,3 @@
-#include <sys/socket.h>
#include "midimonster.h"
int init();
diff --git a/config.c b/config.c
index 6d5fd16..93fb56d 100644
--- a/config.c
+++ b/config.c
@@ -20,6 +20,62 @@ typedef enum {
static backend* current_backend = NULL;
static instance* current_instance = NULL;
+#ifdef _WIN32
+#define GETLINE_BUFFER 4096
+
+static ssize_t getline(char** line, size_t* alloc, FILE* stream){
+ size_t bytes_read = 0;
+ char c;
+ //sanity checks
+ if(!line || !alloc || !stream){
+ return -1;
+ }
+
+ //allocate buffer if none provided
+ if(!*line || !*alloc){
+ *alloc = GETLINE_BUFFER;
+ *line = calloc(GETLINE_BUFFER, sizeof(char));
+ if(!*line){
+ fprintf(stderr, "Failed to allocate memory\n");
+ return -1;
+ }
+ }
+
+ if(feof(stream)){
+ return -1;
+ }
+
+ for(c = fgetc(stream); 1; c = fgetc(stream)){
+ //end of buffer, resize
+ if(bytes_read == (*alloc) - 1){
+ *alloc += GETLINE_BUFFER;
+ *line = realloc(*line, (*alloc) * sizeof(char));
+ if(!*line){
+ fprintf(stderr, "Failed to allocate memory\n");
+ return -1;
+ }
+ }
+
+ //store character
+ (*line)[bytes_read] = c;
+
+ //end of line
+ if(feof(stream) || c == '\n'){
+ //terminate string
+ (*line)[bytes_read + 1] = 0;
+ return bytes_read;
+ }
+
+ //input broken
+ if(ferror(stream) || c < 0){
+ return -1;
+ }
+
+ bytes_read++;
+ }
+}
+#endif
+
static char* config_trim_line(char* in){
ssize_t n;
//trim front
diff --git a/midimonster.c b/midimonster.c
index fb664a4..df27ca3 100644
--- a/midimonster.c
+++ b/midimonster.c
@@ -1,9 +1,14 @@
#include <string.h>
#include <signal.h>
-#include <sys/select.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#define MM_API __attribute__((visibility("default")))
+#else
+#define MM_API __attribute__((dllexport))
+#endif
#include "midimonster.h"
#include "config.h"
#include "backend.h"
@@ -35,11 +40,14 @@ static void signal_handler(int signum){
shutdown_requested = 1;
}
-uint64_t mm_timestamp(){
+uint64_t MM_API mm_timestamp(){
return global_timestamp;
}
static void update_timestamp(){
+ #ifdef _WIN32
+ global_timestamp = GetTickCount();
+ #else
struct timespec current;
if(clock_gettime(CLOCK_MONOTONIC_COARSE, &current)){
fprintf(stderr, "Failed to update global timestamp, time-based processing for some backends may be impaired: %s\n", strerror(errno));
@@ -47,6 +55,7 @@ static void update_timestamp(){
}
global_timestamp = current.tv_sec * 1000 + current.tv_nsec / 1000000;
+ #endif
}
int mm_map_channel(channel* from, channel* to){
@@ -99,7 +108,7 @@ void map_free(){
map = NULL;
}
-int mm_manage_fd(int new_fd, char* back, int manage, void* impl){
+int MM_API mm_manage_fd(int new_fd, char* back, int manage, void* impl){
backend* b = backend_match(back);
size_t u;
@@ -163,7 +172,7 @@ void fds_free(){
fd = NULL;
}
-int mm_channel_event(channel* c, channel_value v){
+int MM_API mm_channel_event(channel* c, channel_value v){
size_t u, p;
//find mapped channels
@@ -229,7 +238,7 @@ static fd_set fds_collect(int* max_fd){
*max_fd = -1;
}
- DBGPF("Building selector set from %zu FDs registered to core\n", fds);
+ DBGPF("Building selector set from %lu FDs registered to core\n", fds);
FD_ZERO(&rv_fds);
for(u = 0; u < fds; u++){
if(fd[u].fd >= 0){
@@ -243,6 +252,17 @@ static fd_set fds_collect(int* max_fd){
return rv_fds;
}
+int platform_initialize(){
+#ifdef _WIN32
+ WSADATA wsa;
+ WORD version = MAKEWORD(2, 2);
+ if(WSAStartup(version, &wsa)){
+ return 1;
+ }
+#endif
+ return 0;
+}
+
int main(int argc, char** argv){
fd_set all_fds, read_fds;
event_collection* secondary = NULL;
@@ -255,6 +275,11 @@ int main(int argc, char** argv){
cfg_file = argv[1];
}
+ if(platform_initialize()){
+ fprintf(stderr, "Failed to perform platform-specific initialization\n");
+ return EXIT_FAILURE;
+ }
+
FD_ZERO(&all_fds);
//initialize backends
if(plugins_load(PLUGINS)){
@@ -316,14 +341,14 @@ int main(int argc, char** argv){
update_timestamp();
//run backend processing, collect events
- DBGPF("%zu backend FDs signaled\n", n);
+ DBGPF("%lu backend FDs signaled\n", n);
if(backends_handle(n, signaled_fds)){
goto bail;
}
while(primary->n){
//swap primary and secondary event collectors
- DBGPF("Swapping event collectors, %zu events in primary\n", primary->n);
+ DBGPF("Swapping event collectors, %lu events in primary\n", primary->n);
for(u = 0; u < sizeof(event_pool) / sizeof(event_collection); u++){
if(primary != event_pool + u){
secondary = primary;
diff --git a/midimonster.h b/midimonster.h
index 7f70f5b..eb118c6 100644
--- a/midimonster.h
+++ b/midimonster.h
@@ -4,6 +4,21 @@
#include <stdlib.h>
#include <stdint.h>
+#ifndef MM_API
+ #ifdef _WIN32
+ #define MM_API __attribute__((dllimport))
+ #else
+ #define MM_API
+ #endif
+#endif
+
+/* GCC ignores the visibility attributes on some API functions, so override visibility */
+#if !defined(_WIN32) && defined(__GNUC__) && !defined(__clang__)
+ #undef MM_API
+ #define MM_API
+ #pragma GCC visibility push(default)
+#endif
+
/* Straight-forward min / max macros */
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
@@ -187,7 +202,7 @@ typedef struct /*_mm_channel_mapping*/ {
/*
* Register a new backend.
*/
-int mm_backend_register(backend b);
+int MM_API mm_backend_register(backend b);
/*
* Provides a pointer to a newly (zero-)allocated instance.
@@ -201,7 +216,8 @@ int mm_backend_register(backend b);
* mmbackend_shutdown procedure of the backend, eg. by querying
* all instances for the backend.
*/
-instance* mm_instance();
+instance* MM_API mm_instance();
+
/*
* Finds an instance matching the specified backend and identifier.
* Since setting an identifier for an instance is optional,
@@ -209,7 +225,8 @@ instance* mm_instance();
* Instance identifiers may for example be set in the backends
* mmbackend_start call.
*/
-instance* mm_instance_find(char* backend, uint64_t ident);
+instance* MM_API mm_instance_find(char* backend, uint64_t ident);
+
/*
* Provides a pointer to a channel structure, pre-filled with
* the provided instance reference and identifier.
@@ -224,30 +241,35 @@ instance* mm_instance_find(char* backend, uint64_t ident);
* this function, the backend will receive a call to its channel_free
* function.
*/
-channel* mm_channel(instance* i, uint64_t ident, uint8_t create);
+channel* MM_API mm_channel(instance* i, uint64_t ident, uint8_t create);
//TODO channel* mm_channel_find()
+
/*
* Register (manage = 1) or unregister (manage = 0) a file descriptor
* to be selected on. The backend will be notified when the descriptor
* becomes ready to read via its registered mmbackend_process_fd call.
*/
-int mm_manage_fd(int fd, char* backend, int manage, void* impl);
+int MM_API mm_manage_fd(int fd, char* backend, int manage, void* impl);
+
/*
* Notifies the core of a channel event. Called by backends to
* inject events gathered from their backing implementation.
*/
-int mm_channel_event(channel* c, channel_value v);
+int MM_API mm_channel_event(channel* c, channel_value v);
+
/*
* Query all active instances for a given backend.
* *i will need to be freed by the caller.
*/
-int mm_backend_instances(char* backend, size_t* n, instance*** i);
+int MM_API mm_backend_instances(char* backend, size_t* n, instance*** i);
+
/*
* Query an internal timestamp, which is updated every core iteration.
* This timestamp should not be used as a performance counter, but can be
* used for timeouting. Resolution is milliseconds.
*/
-uint64_t mm_timestamp();
+uint64_t MM_API mm_timestamp();
+
/*
* Create a channel-to-channel mapping. This API should not
* be used by backends. It is only exported for core modules.
diff --git a/monster.cfg b/monster.cfg
index 2e6f76f..2413f6d 100644
--- a/monster.cfg
+++ b/monster.cfg
@@ -1,18 +1,12 @@
[backend artnet]
bind = 0.0.0.0
-[backend evdev]
+[loopback loop]
[artnet art]
universe = 0
dest = 255.255.255.255
-[evdev mouse]
-input = TPPS
-
-[loopback loop]
-
[map]
-mouse.EV_REL.REL_X > loop.chan0
-art.{3..4}{4..3} > loop.chan{4..3}{3..4}
-art.{1..10} > loop.data{1..10}
+
+art.1+2 > loop.b
diff --git a/plugin.c b/plugin.c
index fc642ac..a452559 100644
--- a/plugin.c
+++ b/plugin.c
@@ -1,11 +1,20 @@
#include <stdio.h>
#include <string.h>
-#include <dlfcn.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
+#include "portability.h"
+#ifdef _WIN32
+#define dlclose FreeLibrary
+#define dlsym GetProcAddress
+#define dlerror() "Failed"
+#define dlopen(lib,ig) LoadLibrary(lib)
+#else
+#include <dlfcn.h>
+#endif
+
#include "plugin.h"
static size_t plugins = 0;
@@ -14,19 +23,29 @@ static void** plugin_handle = NULL;
static int plugin_attach(char* path, char* file){
plugin_init init = NULL;
void* handle = NULL;
+ char* lib = NULL;
+ char* error = NULL;
- char* lib = calloc(strlen(path) + strlen(file) + 1, sizeof(char));
+ lib = calloc(strlen(path) + strlen(file) + 1, sizeof(char));
if(!lib){
fprintf(stderr, "Failed to allocate memory\n");
return 1;
}
-
snprintf(lib, strlen(path) + strlen(file) + 1, "%s%s", path, file);
handle = dlopen(lib, RTLD_NOW);
if(!handle){
- fprintf(stderr, "Failed to load plugin %s: %s\n", lib, dlerror());
+ #ifdef _WIN32
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &error, 0, NULL);
+ #else
+ error = dlerror();
+ #endif
+ fprintf(stderr, "Failed to load plugin %s: %s\n", lib, error);
free(lib);
+ #ifdef _WIN32
+ LocalFree(error);
+ #endif
return 0;
}
@@ -62,6 +81,38 @@ static int plugin_attach(char* path, char* file){
int plugins_load(char* path){
int rv = -1;
+#ifdef _WIN32
+ char* search_expression = calloc(strlen(path) + strlen("*.dll") + 1, sizeof(char));
+ if(!search_expression){
+ fprintf(stderr, "Failed to allocate memory\n");
+ return -1;
+ }
+ snprintf(search_expression, strlen(path) + strlen("*.dll"), "%s*.dll", path);
+
+ WIN32_FIND_DATA result;
+ HANDLE hSearch = FindFirstFile(search_expression, &result);
+
+ if(hSearch == INVALID_HANDLE_VALUE){
+ LPVOID lpMsgBuf = NULL;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
+ fprintf(stderr, "Failed to search for backend plugin files in %s: %s\n", path, lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ return -1;
+ }
+
+ do {
+ if(plugin_attach(path, result.cFileName)){
+ goto load_done;
+ }
+ } while(FindNextFile(hSearch, &result));
+
+ rv = 0;
+load_done:
+ free(search_expression);
+ FindClose(hSearch);
+ return rv;
+#else
struct dirent* entry;
struct stat file_stat;
DIR* directory = opendir(path);
@@ -100,6 +151,7 @@ load_done:
return -1;
}
return rv;
+#endif
}
int plugins_close(){
diff --git a/portability.h b/portability.h
index 25aee01..903ecd8 100644
--- a/portability.h
+++ b/portability.h
@@ -19,3 +19,20 @@
#define be64toh(x) OSSwapBigToHostInt64(x)
#define le64toh(x) OSSwapLittleToHostInt64(x)
#endif
+
+#ifdef _WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+ #include <winsock2.h>
+
+ #define htobe16(x) htons(x)
+ #define be16toh(x) ntohs(x)
+
+ #define htobe32(x) htonl(x)
+ #define be32toh(x) ntohl(x)
+
+ #define htobe64(x) _byteswap_uint64(x)
+ #define htole64(x) (x)
+ #define be64toh(x) _byteswap_uint64(x)
+ #define le64toh(x) (x)
+#endif