diff --git a/.gitignore b/.gitignore index 89f9ac0..d14880a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ out/ +.sass-cache diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f149273 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/lgi"] + path = lib/lgi + url = https://github.com/lgi-devs/lgi diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 14204ef..5d98735 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -7,7 +7,8 @@ "/usr/include/lua5.3", "/usr/include/glib-2.0", "/usr/lib/glib-2.0/include", - "/usr/lib/x86_64-linux-gnu/glib-2.0/include" + "/usr/lib/x86_64-linux-gnu/glib-2.0/include", + "/usr/include/gobject-introspection-1.0" ], "defines": [], "compilerPath": "/usr/bin/gcc", @@ -17,4 +18,4 @@ } ], "version": 4 -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 49936a9..db3ee7e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,10 @@ { - "C_Cpp.errorSquiggles": "Enabled" + "C_Cpp.errorSquiggles": "Enabled", + "files.associations": { + "**/tasks/*.yml": "ansible", + "*.rockspec": "lua", + "*.ld": "lua", + "glib-mainloop.h": "c", + "lua.h": "c" + } } diff --git a/Makefile b/Makefile index 4c1c6a5..450473d 100644 --- a/Makefile +++ b/Makefile @@ -2,31 +2,47 @@ PROJECT = lgi_pulseaudio PREFIX ?= /usr/local BUILD_DIR = out -LUA_VERSION ?= 5.3 +LUA_VERSION ?= 5.1 LUA ?= $(shell command -v lua$(LUA_VERSION)) LUA_BINDIR ?= /usr/bin -# LUA_LIBDIR ?= /usr/lib +LUA_LIBDIR ?= /usr/lib/x86_64-linux-gnu/lua/$(LUA_VERSION) LUA_INCDIR ?= /usr/include/lua$(LUA_VERSION) -INSTALL_BINDIR ?= $(PREFIX)/bin +ifdef LIBDIR +INSTALL_LIBDIR ?= $(LIBDIR) +else INSTALL_LIBDIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) +endif + +ifdef LUADIR +INSTALL_LUADIR ?= $(LUADIR) +else INSTALL_LUADIR ?= $(PREFIX)/share/lua/$(LUA_VERSION) -INSTALL_CONFDIR ?= $(PREFIX)/etc +endif + +ifdef DOCDIR +INSTALL_DOCDIR ?= $(DOCDIR) +else +INSTALL_DOCDIR ?= $(PREFIX)/share/doc/$(PROJECT) +endif CC = gcc PKG_CONFIG ?= $(shell command -v pkg-config) -PKGS = glib-2.0 gobject-2.0 lua$(LUA_VERSION) +PKGS = libpulse-mainloop-glib glib-2.0 gobject-2.0 gobject-introspection-1.0 lua$(LUA_VERSION) CFLAGS ?= -fPIC LIBFLAG ?= -shared CCFLAGS ?= $(CFLAGS) -CCFLAGS += -Wall -g $(shell $(PKG_CONFIG) --cflags $(PKGS)) -I$(LUA_INCDIR) +CCFLAGS += -Wall -g -rdynamic $(shell $(PKG_CONFIG) --cflags $(PKGS)) -I$(LUA_INCDIR) -I"./" -LIBS = -L"$(LUA_LIBDIR)" -L$(shell dirname "$(shell $(CC) -print-libgcc-file-name)") +LIBS = -L$(shell dirname "$(shell $(CC) -print-libgcc-file-name)") -L"$(LUA_LIBDIR)" -L"./" LIBS += $(shell $(PKG_CONFIG) --libs $(PKGS)) -OBJS = $(shell find src -type f -iname '*.c' | sed 's/\(.*\)\.c$$/$(BUILD_DIR)\/\1\.so/') +OBJS = $(shell find src -type f -iname '*.c' | sed 's/\(.*\)\.c$$/$(BUILD_DIR)\/\1\.o/') +LGI_OBJS = $(shell find lib/lgi/lgi -type f -iname '*.c' | sed 's/\(.*\)\.c$$/\1\.o/') + +TARGET = $(BUILD_DIR)/$(PROJECT).so ifdef CI CHECK_ARGS ?= --formatter TAP @@ -37,14 +53,17 @@ endif .PHONY: clean doc doc-content doc-styles install test check rock -build: $(OBJS) +build: build-lgi $(TARGET) + +build-lgi: + make -C lib/lgi $(BUILD_DIR)/%.o: %.c @mkdir -p $(shell dirname "$@") $(CC) -c $(CCFLAGS) $< -o $@ -%.so: %.o - $(CC) $(LIBFLAG) -o $@ $< $(LIBS) +$(TARGET): $(OBJS) + $(CC) $(LIBFLAG) -o $@ $(OBJS) $(LGI_OBJS) $(LIBS) doc-styles: @printf "\e[1;97mGenerate stylesheet\e[0m\n" @@ -66,21 +85,24 @@ clean: rm -r out/ install: build doc - @printf "\e[1;97mInstall C libraries\e[0m\n" - find $(BUILD_DIR)/src -type f -iname '*.so' | xargs install -vDm 644 -t $(INSTALL_LIBDIR)/$(PROJECT) + @printf "\e[1;97mInstall C library\e[0m\n" + xargs install -vDm 644 -t $(INSTALL_LIBDIR)/$(PROJECT) $(TARGET) - @printf "\e[1;97mInstall Lua libraries\e[0m\n" - find src/ -type f -iname '*.lua' | xargs install -vDm 644 -t $(INSTALL_LUADIR)/$(PROJECT) + # @printf "\e[1;97mInstall Lua libraries\e[0m\n" + # find src/ -type f -iname '*.lua' | xargs install -vDm 644 -t $(INSTALL_LUADIR)/$(PROJECT) @printf "\e[1;97mInstall documentation\e[0m\n" - install -vd $(PREFIX)/share/doc/$(PROJECT) - cp -vr $(BUILD_DIR)/doc/* $(PREFIX)/share/doc/$(PROJECT) + install -vd $(INSTALL_DOCDIR) + cp -vr $(BUILD_DIR)/doc/* $(INSTALL_DOCDIR) check: - find src/ -iname '*.lua' | xargs luacheck $(CHECK_ARGS) + @echo "Nothing to do" test: busted --config-file=.busted.lua --lua=$(LUA) $(TEST_ARGS) rock: luarocks --local --lua-version $(LUA_VERSION) make rocks/lgi-pulseaudio-scm-1.rockspec + +run: build + env LUA_CPATH="./out/?.so;${LUA_CPATH}" lua5.1 test.lua diff --git a/lib/lgi b/lib/lgi new file mode 160000 index 0000000..340a250 --- /dev/null +++ b/lib/lgi @@ -0,0 +1 @@ +Subproject commit 340a250ab0dfc157fe027f7a29eafda9b8572e5c diff --git a/rocks/lgi-pulseaudio-scm-1.rockspec b/rocks/lgi-pulseaudio-scm-1.rockspec index afa3f1a..64e0295 100644 --- a/rocks/lgi-pulseaudio-scm-1.rockspec +++ b/rocks/lgi-pulseaudio-scm-1.rockspec @@ -33,6 +33,7 @@ build = { INSTALL_LIBDIR="$(LIBDIR)", INSTALL_LUADIR="$(LUADIR)", INSTALL_CONFDIR="$(CONFDIR)", + INSTALL_DOCDIR="$(DOCDIR)", }, copy_directories = { "spec" diff --git a/src/lgi-pulseaudio/init.c b/src/lgi-pulseaudio/init.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/lgi-pulseaudio/pulseaudio.c b/src/lgi-pulseaudio/pulseaudio.c new file mode 100644 index 0000000..eeb54c9 --- /dev/null +++ b/src/lgi-pulseaudio/pulseaudio.c @@ -0,0 +1,143 @@ +/// libpulse bindings. +// +// @module pulseaudio +#include "lib/lgi/lgi/lgi.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "lgi-pulseaudio" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define LUA_MOD_EXPORT __declspec(dllexport) +#else +#define LUA_MOD_EXPORT extern +#endif + +#define LUA_PULSEAUDIO "pulseaudio" + +#if LUA_VERSION_NUM <= 501 +// Shamelessly copied from Lua 5.3 source. +// TODO: Is there any official way to do this in 5.1? +void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + if (l->func == NULL) /* place holder? */ + lua_pushboolean(L, 0); + else { + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + } + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + +#define luaL_newlib(L,l) (luaL_register(L,LUA_PULSEAUDIO,l)) +#endif + +typedef struct pulseaudio { + pa_glib_mainloop* mainloop; +} pulseaudio; + + +static int +pulseaudio_new(lua_State* L) +{ + printf("get new stuff: %d\n", lua_gettop(L)); + GMainContext* ctx = NULL; + + if (lua_gettop(L) > 0) { + luaL_argcheck(L, lua_isuserdata(L, 1), 1, NULL); + printf("it's userdata\n"); + + lua_getfield(L, -1, "type"); + GITypeInfo* type_info = *(GITypeInfo **) luaL_checkudata(L, -1, LGI_GI_INFO); + luaL_argcheck(L, type_info != NULL && GI_IS_TYPE_INFO(type_info), 1, "paramter has no type info"); + // TODO: Check if it's really a GMainContext + printf("type: %s\n", g_base_info_get_name(type_info)); + // Pop the `type` field + lua_pop(L, 1); + + lua_pop(L, lgi_marshal_2c(L, type_info, NULL, GI_TRANSFER_NOTHING, (gpointer) ctx, 1, 0, NULL, NULL)); + ctx = (GMainContext*) lua_touserdata(L, 1); + } else { + ctx = g_main_context_default(); + + if (ctx == NULL) { + lua_pushfstring(L, "Failed to accquire default GLib Main Context. Are we running in a Main Loop?"); + lua_error(L); + return 0; + } + } + printf("got some context\n"); + + pulseaudio* pa = lua_newuserdata (L, sizeof(pulseaudio)); + if (!pa) { + return luaL_error(L, "failed to create pulseaudio userdata"); + } + pa->mainloop = pa_glib_mainloop_new(ctx); + luaL_getmetatable(L, LUA_PULSEAUDIO); + lua_setmetatable(L, -2); + return 1; +} + + +// TODO: Implement `__gc` meta method to free the inner `pa_glib_mainloop` +static const struct luaL_Reg pulseaudio_mt [] = { + {NULL, NULL} +}; + +static const struct luaL_Reg pulseaudio_lib [] = { + {"new", pulseaudio_new}, + {NULL, NULL} +}; + + +// Shamelessly stolen from Lua 5.3 source code +// Uses Lua's `require` +static int dolibrary(lua_State *L, const char *name) { + int status; + int base; + + base = lua_gettop(L); + + lua_getglobal(L, "require"); + lua_pushstring(L, name); + status = lua_pcall(L, 1, 1, 0); + + // Data for the `require` call is no longer needed + lua_insert(L, base); + lua_pop(L, 2); + + if (status == 0) { + lua_setglobal(L, name); + lua_pop(L, 1); + } + + return status; +} + +LUA_MOD_EXPORT int luaopen_lgi_pulseaudio(lua_State* L) +{ + if (dolibrary(L, "lgi") != 0) { + lua_pushfstring(L, "failed to load library 'lgi'"); + lua_error(L); + return 0; + } + + luaL_newmetatable(L, LUA_PULSEAUDIO); + luaL_setfuncs(L, pulseaudio_mt, 0); + + luaL_newlib(L, pulseaudio_lib); + return 1; +} diff --git a/test.lua b/test.lua new file mode 100644 index 0000000..be90399 --- /dev/null +++ b/test.lua @@ -0,0 +1,8 @@ +local lgi = require("lgi") +local pulseaudio = require("lgi_pulseaudio") + +local loop = lgi.GLib.MainLoop.new() + +print(pulseaudio.new) +print(loop:get_context()) +-- print(pulseaudio.new(loop:get_context()))