Compare commits
No commits in common. "master" and "wip/lgi" have entirely different histories.
41 changed files with 366 additions and 4680 deletions
|
@ -1,6 +1,7 @@
|
||||||
return {
|
return {
|
||||||
default = {
|
default = {
|
||||||
verbose = true,
|
verbose = true,
|
||||||
|
helper = "./spec/_helper.lua",
|
||||||
lpath = "./src/?.lua;./src/?/init.lua;./src/?/?.lua;./tests/?.lua",
|
lpath = "./src/?.lua;./src/?/init.lua;./src/?/?.lua;./tests/?.lua",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
mkdir -p "${BUILD_DIR:-./build}"
|
|
||||||
|
|
||||||
CMAKE_ARGS=""
|
|
||||||
|
|
||||||
if [ -n "$CI" ]; then
|
|
||||||
CMAKE_ARGS="-DCI=ON"
|
|
||||||
fi
|
|
||||||
|
|
||||||
sudo enable-lua ${LUA_VERSION:-5.1}
|
|
||||||
|
|
||||||
make -j $(nproc) -C ${SOURCE_DIR:-.} BUILD_DIR=${BUILD_DIR:-./build} LUA_VERSION=${LUA_VERSION:-5.1} LUA=lua doc
|
|
||||||
|
|
||||||
tar -czf doc.tar.gz -C ${BUILD_DIR:-./build}/doc .
|
|
||||||
|
|
||||||
curl -T doc.tar.gz -XPOST --user ${DOCS_CREDENTIALS} https://${DOCS_HOST}/publish/lua-libpulse-glib
|
|
||||||
|
|
||||||
sudo disable-lua ${LUA_VERSION:-5.1}
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
---
|
|
||||||
platform: linux
|
|
||||||
|
|
||||||
image_resource:
|
|
||||||
type: registry-image
|
|
||||||
source:
|
|
||||||
repository: registry.local:5000/lua-clib-pulse
|
|
||||||
tag: latest
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
- name: repo
|
|
||||||
|
|
||||||
params:
|
|
||||||
CI: true
|
|
||||||
SOURCE_DIR: repo
|
|
||||||
BUILD_DIR: /tmp/build-output
|
|
||||||
DOCS_HOST: ((doc-host))
|
|
||||||
DOCS_CREDENTIALS: "((deploy-user)):((deploy-password))"
|
|
||||||
|
|
||||||
run:
|
|
||||||
path: repo/.ci/deploy-docs.sh
|
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
---
|
|
||||||
resources:
|
|
||||||
- name: repo
|
|
||||||
type: git
|
|
||||||
icon: github
|
|
||||||
source:
|
|
||||||
uri: https://git.sclu1034.dev/lucas/lua-libpulse-glib
|
|
||||||
branch: master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- name: test
|
|
||||||
plan:
|
|
||||||
- get: repo
|
|
||||||
trigger: true
|
|
||||||
- in_parallel:
|
|
||||||
- task: run-test-5.1
|
|
||||||
file: repo/.ci/test.yml
|
|
||||||
vars:
|
|
||||||
lua-version: 5.1
|
|
||||||
- task: run-test-5.2
|
|
||||||
file: repo/.ci/test.yml
|
|
||||||
vars:
|
|
||||||
lua-version: 5.2
|
|
||||||
- task: run-test-5.3
|
|
||||||
file: repo/.ci/test.yml
|
|
||||||
vars:
|
|
||||||
lua-version: 5.3
|
|
||||||
# There is no rock for 5.4 LGI, yet. And I don't want to build that from source.
|
|
||||||
- try:
|
|
||||||
task: run-test-5.4
|
|
||||||
file: repo/.ci/test.yml
|
|
||||||
vars:
|
|
||||||
lua-version: 5.4
|
|
||||||
|
|
||||||
- name: rock
|
|
||||||
plan:
|
|
||||||
- get: repo
|
|
||||||
trigger: true
|
|
||||||
- in_parallel:
|
|
||||||
- task: run-rock-5.1
|
|
||||||
file: repo/.ci/rock.yml
|
|
||||||
vars:
|
|
||||||
lua-version: 5.1
|
|
||||||
- task: run-rock-5.2
|
|
||||||
file: repo/.ci/rock.yml
|
|
||||||
vars:
|
|
||||||
lua-version: 5.2
|
|
||||||
- task: run-rock-5.3
|
|
||||||
file: repo/.ci/rock.yml
|
|
||||||
vars:
|
|
||||||
lua-version: 5.3
|
|
||||||
# There is no rock for 5.4 LGI, yet. And I don't want to build that from source.
|
|
||||||
- try:
|
|
||||||
task: run-rock-5.4
|
|
||||||
file: repo/.ci/rock.yml
|
|
||||||
vars:
|
|
||||||
lua-version: 5.4
|
|
||||||
|
|
||||||
- name: doc
|
|
||||||
plan:
|
|
||||||
- get: repo
|
|
||||||
trigger: true
|
|
||||||
- task: deploy-docs
|
|
||||||
file: repo/.ci/deploy-docs.yml
|
|
||||||
vars:
|
|
||||||
doc-host: ((doc-host))
|
|
||||||
deploy-user: ((deploy-user))
|
|
||||||
deploy-password: ((deploy-password))
|
|
14
.ci/rock.sh
14
.ci/rock.sh
|
@ -1,14 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
sudo enable-lua ${LUA_VERSION:-5.1}
|
|
||||||
eval "$(luarocks --lua-version ${LUA_VERSION:-5.1} path)"
|
|
||||||
|
|
||||||
luarocks --lua-version ${LUA_VERSION:-5.1} install lgi
|
|
||||||
luarocks --lua-version ${LUA_VERSION:-5.1} install ldoc
|
|
||||||
luarocks --lua-version ${LUA_VERSION:-5.1} install lua-discount
|
|
||||||
|
|
||||||
luarocks --lua-version ${LUA_VERSION:-5.1} make rocks/lua-libpulse-glib-scm-1.rockspec
|
|
||||||
|
|
||||||
sudo disable-lua ${LUA_VERSION:-5.1}
|
|
20
.ci/rock.yml
20
.ci/rock.yml
|
@ -1,20 +0,0 @@
|
||||||
|
|
||||||
---
|
|
||||||
platform: linux
|
|
||||||
|
|
||||||
image_resource:
|
|
||||||
type: registry-image
|
|
||||||
source:
|
|
||||||
repository: registry.local:5000/lua-clib-pulse
|
|
||||||
tag: latest
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
- name: repo
|
|
||||||
|
|
||||||
params:
|
|
||||||
CI: true
|
|
||||||
LUA_VERSION: ((lua-version))
|
|
||||||
|
|
||||||
run:
|
|
||||||
path: .ci/rock.sh
|
|
||||||
dir: repo
|
|
21
.ci/test.sh
21
.ci/test.sh
|
@ -1,21 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
mkdir -p "${BUILD_DIR:-./build}"
|
|
||||||
|
|
||||||
CMAKE_ARGS=""
|
|
||||||
|
|
||||||
if [ -n "$CI" ]; then
|
|
||||||
CMAKE_ARGS="-DCI=ON"
|
|
||||||
fi
|
|
||||||
|
|
||||||
sudo enable-lua ${LUA_VERSION:-5.1}
|
|
||||||
eval "$(luarocks --lua-version ${LUA_VERSION:-5.1} path)"
|
|
||||||
|
|
||||||
luarocks --lua-version ${LUA_VERSION:-5.1} install busted
|
|
||||||
luarocks --lua-version ${LUA_VERSION:-5.1} install lgi
|
|
||||||
|
|
||||||
make -j $(nproc) -C ${SOURCE_DIR:-.} BUILD_DIR=${BUILD_DIR:-./build} LUA_VERSION=${LUA_VERSION:-5.1} LUA=lua build
|
|
||||||
|
|
||||||
sudo disable-lua ${LUA_VERSION:-5.1}
|
|
20
.ci/test.yml
20
.ci/test.yml
|
@ -1,20 +0,0 @@
|
||||||
---
|
|
||||||
platform: linux
|
|
||||||
|
|
||||||
image_resource:
|
|
||||||
type: registry-image
|
|
||||||
source:
|
|
||||||
repository: registry.local:5000/lua-clib-pulse
|
|
||||||
tag: latest
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
- name: repo
|
|
||||||
|
|
||||||
params:
|
|
||||||
CI: true
|
|
||||||
SOURCE_DIR: repo
|
|
||||||
BUILD_DIR: /tmp/build-output
|
|
||||||
LUA_VERSION: ((lua-version))
|
|
||||||
|
|
||||||
run:
|
|
||||||
path: repo/.ci/test.sh
|
|
|
@ -1,75 +0,0 @@
|
||||||
---
|
|
||||||
# kak: filetype=yaml
|
|
||||||
Language: Cpp
|
|
||||||
BasedOnStyle: LLVM
|
|
||||||
AlignArrayOfStructures: Left
|
|
||||||
AlignConsecutiveMacros: AcrossComments
|
|
||||||
AlignConsecutiveAssignments: None
|
|
||||||
AlignConsecutiveBitFields: AcrossComments
|
|
||||||
AlignConsecutiveDeclarations: None
|
|
||||||
AllowAllArgumentsOnNextLine: false
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: false
|
|
||||||
AllowShortEnumsOnASingleLine: false
|
|
||||||
AllowShortFunctionsOnASingleLine: None
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
|
||||||
AttributeMacros:
|
|
||||||
- __capability
|
|
||||||
BinPackArguments: false
|
|
||||||
BinPackParameters: false
|
|
||||||
BreakBeforeBinaryOperators: NonAssignment
|
|
||||||
BreakBeforeInheritanceComma: false
|
|
||||||
BreakInheritanceList: AfterComma
|
|
||||||
ColumnLimit: 120
|
|
||||||
Cpp11BracedListStyle: false
|
|
||||||
EmptyLineBeforeAccessModifier: Always
|
|
||||||
FixNamespaceComments: true
|
|
||||||
IncludeBlocks: Regroup
|
|
||||||
IncludeCategories:
|
|
||||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
|
||||||
Priority: 2
|
|
||||||
SortPriority: 0
|
|
||||||
CaseSensitive: false
|
|
||||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
|
||||||
Priority: 3
|
|
||||||
SortPriority: 0
|
|
||||||
CaseSensitive: false
|
|
||||||
- Regex: '.*'
|
|
||||||
Priority: 1
|
|
||||||
SortPriority: 0
|
|
||||||
CaseSensitive: false
|
|
||||||
IncludeIsMainRegex: '(_test)?$'
|
|
||||||
IncludeIsMainSourceRegex: ''
|
|
||||||
IndentExternBlock: NoIndent
|
|
||||||
IndentWidth: 4
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
||||||
MaxEmptyLinesToKeep: 2
|
|
||||||
# PackConstructorInitializers: CurrentLine
|
|
||||||
PointerAlignment: Left
|
|
||||||
ReferenceAlignment: Right
|
|
||||||
ReflowComments: true
|
|
||||||
# SeparateDefinitionBlocks: Leave
|
|
||||||
SpaceAfterCStyleCast: true
|
|
||||||
SpaceAfterLogicalNot: false
|
|
||||||
SpaceAfterTemplateKeyword: false
|
|
||||||
SpaceBeforeAssignmentOperators: true
|
|
||||||
SpaceBeforeCaseColon: false
|
|
||||||
SpaceBeforeCpp11BracedList: false
|
|
||||||
SpaceBeforeCtorInitializerColon: true
|
|
||||||
SpaceBeforeInheritanceColon: true
|
|
||||||
SpaceBeforeParens: ControlStatements
|
|
||||||
SpaceAroundPointerQualifiers: Default
|
|
||||||
SpaceBeforeRangeBasedForLoopColon: true
|
|
||||||
SpaceInEmptyBlock: false
|
|
||||||
SpaceInEmptyParentheses: false
|
|
||||||
SpacesBeforeTrailingComments: 1
|
|
||||||
SpacesInAngles: Never
|
|
||||||
SpacesInConditionalStatement: false
|
|
||||||
SpacesInContainerLiterals: true
|
|
||||||
SpacesInCStyleCastParentheses: false
|
|
||||||
SpacesInLineCommentPrefix:
|
|
||||||
Minimum: 1
|
|
||||||
Maximum: -1
|
|
||||||
SpacesInParentheses: false
|
|
||||||
SpacesInSquareBrackets: false
|
|
||||||
SpaceBeforeSquareBrackets: false
|
|
||||||
TabWidth: 4
|
|
|
@ -1,2 +0,0 @@
|
||||||
out/
|
|
||||||
.sass-cache/
|
|
79
.github/workflows/ci.yml
vendored
Normal file
79
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
name: Lint & Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master, staging, trying ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CI: true
|
||||||
|
DEBIAN: noninteractive
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
- uses: leafo/gh-actions-lua@v8.0.0
|
||||||
|
- uses: leafo/gh-actions-luarocks@v4.0.0
|
||||||
|
|
||||||
|
- name: Install LuaCheck
|
||||||
|
run: luarocks install luacheck
|
||||||
|
|
||||||
|
- name: Run checks
|
||||||
|
run: make check LUA_VERSION=${{ matrix.lua_version }}
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- lua_version: '5.1'
|
||||||
|
- lua_version: '5.2'
|
||||||
|
- lua_version: '5.3'
|
||||||
|
- lua_version: 'luajit'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
- uses: leafo/gh-actions-lua@v8.0.0
|
||||||
|
with:
|
||||||
|
luaVersion: ${{ matrix.lua_version }}
|
||||||
|
- uses: leafo/gh-actions-luarocks@v4.0.0
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: /bin/bash -o errexit -o pipefail -o xtrace {0}
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y --no-install-recommends gobject-introspection libgirepository1.0-dev libpulse-dev
|
||||||
|
luarocks install busted
|
||||||
|
luarocks install lgi
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: make test LUA_VERSION=${{ matrix.lua_version }}
|
||||||
|
|
||||||
|
rock:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs: [test, check]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- lua_version: '5.1'
|
||||||
|
- lua_version: '5.2'
|
||||||
|
- lua_version: '5.3'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
- uses: leafo/gh-actions-lua@v8.0.0
|
||||||
|
with:
|
||||||
|
luaVersion: ${{ matrix.lua_version }}
|
||||||
|
- uses: leafo/gh-actions-luarocks@v4.0.0
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: /bin/bash -o errexit -o pipefail -o xtrace {0}
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y --no-install-recommends gobject-introspection libgirepository1.0-dev
|
||||||
|
|
||||||
|
- name: Make rock
|
||||||
|
run: make rock LUA_VERSION=${{ matrix.lua_version }}
|
34
.github/workflows/deploy-to-pages.yml
vendored
Normal file
34
.github/workflows/deploy-to-pages.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
name: Deploy docs
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['master']
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEBIAN: noninteractive
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-docs:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
- uses: leafo/gh-actions-lua@v8.0.0
|
||||||
|
- uses: leafo/gh-actions-luarocks@v4.0.0
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 'lts/*'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
luarocks install ldoc
|
||||||
|
luarocks install lua-discount
|
||||||
|
npm install --global sass
|
||||||
|
|
||||||
|
- name: Generate documentation
|
||||||
|
run: make doc
|
||||||
|
|
||||||
|
- name: Deploy to gh-pages
|
||||||
|
uses: JamesIves/github-pages-deploy-action@v4.2.5
|
||||||
|
with:
|
||||||
|
branch: gh-pages
|
||||||
|
folder: out/doc
|
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -1,12 +1,2 @@
|
||||||
out/
|
out/
|
||||||
|
|
||||||
.gdb_history
|
|
||||||
.gdbinit
|
|
||||||
lua.debug
|
|
||||||
|
|
||||||
.cache/clangd
|
|
||||||
compile_commands.json
|
|
||||||
.sass-cache
|
.sass-cache
|
||||||
|
|
||||||
*.rockspec.asc
|
|
||||||
*.src.rock
|
|
||||||
|
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "lib/lgi"]
|
||||||
|
path = lib/lgi
|
||||||
|
url = https://github.com/lgi-devs/lgi
|
21
.vscode/c_cpp_properties.json
vendored
Normal file
21
.vscode/c_cpp_properties.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Linux",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/**",
|
||||||
|
"/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/include/gobject-introspection-1.0"
|
||||||
|
],
|
||||||
|
"defines": [],
|
||||||
|
"compilerPath": "/usr/bin/gcc",
|
||||||
|
"cStandard": "c11",
|
||||||
|
"cppStandard": "c++14",
|
||||||
|
"intelliSenseMode": "linux-clang-x64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
10
.vscode/settings.json
vendored
Normal file
10
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"C_Cpp.errorSquiggles": "Enabled",
|
||||||
|
"files.associations": {
|
||||||
|
"**/tasks/*.yml": "ansible",
|
||||||
|
"*.rockspec": "lua",
|
||||||
|
"*.ld": "lua",
|
||||||
|
"glib-mainloop.h": "c",
|
||||||
|
"lua.h": "c"
|
||||||
|
}
|
||||||
|
}
|
72
Makefile
72
Makefile
|
@ -1,4 +1,4 @@
|
||||||
PROJECT = lua_libpulse_glib
|
PROJECT = lgi_pulseaudio
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
BUILD_DIR = out
|
BUILD_DIR = out
|
||||||
|
|
||||||
|
@ -40,11 +40,10 @@ CCFLAGS += -Wall -g -rdynamic $(shell $(PKG_CONFIG) --cflags $(PKGS)) -I$(LUA_IN
|
||||||
LIBS = -L$(shell dirname "$(shell $(CC) -print-libgcc-file-name)") -L"$(LUA_LIBDIR)" -L"./"
|
LIBS = -L$(shell dirname "$(shell $(CC) -print-libgcc-file-name)") -L"$(LUA_LIBDIR)" -L"./"
|
||||||
LIBS += $(shell $(PKG_CONFIG) --libs $(PKGS))
|
LIBS += $(shell $(PKG_CONFIG) --libs $(PKGS))
|
||||||
OBJS = $(shell find src -type f -iname '*.c' | sed 's/\(.*\)\.c$$/$(BUILD_DIR)\/\1\.o/')
|
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
|
TARGET = $(BUILD_DIR)/$(PROJECT).so
|
||||||
|
|
||||||
LUA_CPATH = $(shell echo "$${PWD}/$(BUILD_DIR)/?.so;$${LUA_CPATH}")
|
|
||||||
|
|
||||||
ifdef CI
|
ifdef CI
|
||||||
CHECK_ARGS ?= --formatter TAP
|
CHECK_ARGS ?= --formatter TAP
|
||||||
TEST_ARGS ?= --output=TAP
|
TEST_ARGS ?= --output=TAP
|
||||||
|
@ -52,67 +51,58 @@ TEST_ARGS ?= --output=TAP
|
||||||
CCFLAGS += -Werror
|
CCFLAGS += -Werror
|
||||||
endif
|
endif
|
||||||
|
|
||||||
bold := $(shell tput bold)
|
.PHONY: clean doc doc-content doc-styles install test check rock
|
||||||
orange := $(shell tput setaf 7)
|
|
||||||
title := $(bold)$(orange)
|
|
||||||
reset := $(shell tput sgr0)
|
|
||||||
|
|
||||||
.PHONY: all clean doc doc-content doc-styles install uninstall test check rock
|
build: build-lgi $(TARGET)
|
||||||
|
|
||||||
all: build doc
|
build-lgi:
|
||||||
|
make -C lib/lgi
|
||||||
build: $(TARGET)
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.c
|
$(BUILD_DIR)/%.o: %.c
|
||||||
@mkdir -p $(shell dirname "$@")
|
@mkdir -p $(shell dirname "$@")
|
||||||
@echo "$(title)$(CC) $< -o $@$(reset)"
|
$(CC) -c $(CCFLAGS) $< -o $@
|
||||||
@$(CC) -c $(CCFLAGS) $< -o $@
|
|
||||||
|
|
||||||
$(TARGET): $(OBJS)
|
$(TARGET): $(OBJS)
|
||||||
@echo "$(title)$(CC) -o $@$(reset)"
|
$(CC) $(LIBFLAG) -o $@ $(OBJS) $(LGI_OBJS) $(LIBS)
|
||||||
@$(CC) $(LIBFLAG) -o $@ $(OBJS) $(LIBS)
|
|
||||||
|
|
||||||
$(BUILD_DIR)/doc/index.html:
|
doc-styles:
|
||||||
@mkdir -p "$(BUILD_DIR)/doc" "$(BUILD_DIR)/src"
|
@printf "\e[1;97mGenerate stylesheet\e[0m\n"
|
||||||
@echo "$(title)Preprocess sources$(reset)"
|
|
||||||
sh tools/process_docs.sh "$(BUILD_DIR)"
|
|
||||||
@echo "$(title)Generate documentation$(reset)"
|
|
||||||
ldoc --config=doc/config.ld --dir "$(BUILD_DIR)/doc" --project $(PROJECT) "$(BUILD_DIR)/src"
|
|
||||||
|
|
||||||
$(BUILD_DIR)/doc/ldoc.css: doc/ldoc.scss
|
|
||||||
@mkdir -p "$(BUILD_DIR)/doc"
|
|
||||||
@echo "$(title)Generate stylesheet$(reset)"
|
|
||||||
sass doc/ldoc.scss $(BUILD_DIR)/doc/ldoc.css
|
sass doc/ldoc.scss $(BUILD_DIR)/doc/ldoc.css
|
||||||
|
|
||||||
doc-styles: $(BUILD_DIR)/doc/ldoc.css
|
doc-content:
|
||||||
|
@mkdir -p "$(BUILD_DIR)/doc" "$(BUILD_DIR)/src"
|
||||||
|
@printf "\e[1;97mPreprocess sources\e[0m\n"
|
||||||
|
sh tools/process_docs.sh "$(BUILD_DIR)"
|
||||||
|
@printf "\e[1;97mGenerate documentation\e[0m\n"
|
||||||
|
ldoc --config=doc/config.ld --dir "$(BUILD_DIR)/doc" --project $(PROJECT) "$(BUILD_DIR)/src"
|
||||||
|
|
||||||
doc-content: $(BUILD_DIR)/doc/index.html
|
doc: doc-content doc-styles
|
||||||
|
ifdef CI
|
||||||
doc: doc-styles doc-content
|
touch "$(BUILD_DIR)/doc/.nojekyll"
|
||||||
|
endif
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -r out/
|
rm -r out/
|
||||||
|
|
||||||
install: build doc
|
install: build doc
|
||||||
@echo "$(title)Install C library\033[0m"
|
@printf "\e[1;97mInstall C library\e[0m\n"
|
||||||
install -vDm 644 -t $(INSTALL_LIBDIR) $(TARGET)
|
xargs install -vDm 644 -t $(INSTALL_LIBDIR)/$(PROJECT) $(TARGET)
|
||||||
|
|
||||||
@echo "$(title)Install documentation\033[0m"
|
# @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 $(INSTALL_DOCDIR)
|
install -vd $(INSTALL_DOCDIR)
|
||||||
cp -vr $(BUILD_DIR)/doc/* $(INSTALL_DOCDIR)
|
cp -vr $(BUILD_DIR)/doc/* $(INSTALL_DOCDIR)
|
||||||
|
|
||||||
uninstall:
|
|
||||||
rm $(INSTALL_LIBDIR)/$(PROJECT).so
|
|
||||||
rm -r $(INSTALL_DOCDIR)
|
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@echo "Nothing to do"
|
@echo "Nothing to do"
|
||||||
|
|
||||||
spec: build
|
test:
|
||||||
busted --config-file=.busted.lua --lua=$(LUA) $(TEST_ARGS)
|
busted --config-file=.busted.lua --lua=$(LUA) $(TEST_ARGS)
|
||||||
|
|
||||||
test: build
|
|
||||||
$(LUA) test.lua
|
|
||||||
|
|
||||||
rock:
|
rock:
|
||||||
luarocks --local --lua-version $(LUA_VERSION) make rocks/lua-libpulse-glib-scm-1.rockspec
|
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
|
||||||
|
|
50
README.adoc
50
README.adoc
|
@ -1,10 +1,11 @@
|
||||||
= lua-libpulse-glib
|
= lgi-pulseaudio
|
||||||
:idprefix:
|
:idprefix:
|
||||||
:idseparator: -
|
:idseparator: -
|
||||||
ifdef::env-github,env-browser[]
|
ifdef::env-github,env-browser[]
|
||||||
:toc: macro
|
:toc: macro
|
||||||
:toclevels: 1
|
:toclevels: 1
|
||||||
endif::[]
|
endif::[]
|
||||||
|
ifdef::env-github[]
|
||||||
:branch: master
|
:branch: master
|
||||||
:status:
|
:status:
|
||||||
:outfilesuffix: .adoc
|
:outfilesuffix: .adoc
|
||||||
|
@ -14,56 +15,31 @@ endif::[]
|
||||||
:note-caption: :paperclip:
|
:note-caption: :paperclip:
|
||||||
:tip-caption: :bulb:
|
:tip-caption: :bulb:
|
||||||
:warning-caption: :warning:
|
:warning-caption: :warning:
|
||||||
:url-ci: https://ci.sclu1034.dev/teams/main/pipelines/lua-libpulse-glib
|
endif::[]
|
||||||
:url-ci-badge: https://ci.sclu1034.dev/api/v1/teams/main/pipelines/lua-libpulse-glib/badge
|
:url-ci-github: https://github.com/sclu1034/lgi-pulseaudio/actions
|
||||||
|
:url-ci-badge-github: https://img.shields.io/github/workflow/status/sclu1034/lgi-pulseaudio/Lint%20&%20Test?style=flat-square
|
||||||
:url-license-badge: https://img.shields.io/badge/license-GPLv3-brightgreen?style=flat-square
|
:url-license-badge: https://img.shields.io/badge/license-GPLv3-brightgreen?style=flat-square
|
||||||
:url-luarocks-badge: https://img.shields.io/luarocks/v/sclu1034/lua-libpulse-glib?style=flat-square
|
:url-luarocks-badge: https://img.shields.io/luarocks/v/sclu1034/lgi-pulseaudio?style=flat-square
|
||||||
:url-luarocks-link: https://luarocks.org/modules/sclu1034/lua-libpulse-glib
|
:url-luarocks-link: https://luarocks.org/modules/sclu1034/lgi-pulseaudio
|
||||||
|
|
||||||
image:{url-license-badge}[License]
|
image:{url-license-badge}[License]
|
||||||
ifdef::status[]
|
ifdef::status[]
|
||||||
image:{url-ci-badge}[Build Status (Concourse CI), link={url-ci}]
|
image:{url-ci-badge-github}[Build Status (GitHub Actions), link={url-ci-github}]
|
||||||
endif::[]
|
endif::[]
|
||||||
image:{url-luarocks-badge}[LuaRocks Package, link={url-luarocks-link}]
|
image:{url-luarocks-badge}[LuaRocks Package, link={url-luarocks-link}]
|
||||||
|
|
||||||
https://freedesktop.org/software/pulseaudio/doxygen/index.html[libpulse] bindings for use with a GLib MainLoop via
|
https://freedesktop.org/software/pulseaudio/doxygen/index.html[libpulse] bindings for use with a GLib MainLoop via
|
||||||
https://github.com/lgi-devs/lgi/[LGI].
|
https://github.com/lgi-devs/lgi/[LGI].
|
||||||
|
|
||||||
The project is still in a development state. It hasn't been fully tested yet, and not all functions from libpulse
|
== Installation
|
||||||
have a corresponding binding yet.
|
|
||||||
Please do report anything that's missing or not working correctly.
|
|
||||||
|
|
||||||
== Quick Start
|
_lgi-pulseaudio_ is available via LuaRocks:
|
||||||
|
|
||||||
Install https://github.com/lgi-devs/lgi[lgi] and _lua_libpulse_glib_ from LuaRocks:
|
|
||||||
|
|
||||||
[source,shell]
|
[source,shell]
|
||||||
----
|
----
|
||||||
luarocks install lgi
|
luarocks install lgi-pulseaudio
|
||||||
luarocks install --dev lua-libpulse-glib
|
|
||||||
----
|
----
|
||||||
|
|
||||||
[source,lua]
|
When cloning/vendoring the library, the following dependencies are required:
|
||||||
----
|
|
||||||
local lgi = require("lgi")
|
|
||||||
local pulseaudio = require("lua_libpulse_glib")
|
|
||||||
local ppretty = require("pl.pretty")
|
|
||||||
|
|
||||||
local pa = pulseaudio.new()
|
* https://github.com/lgi-devs/lgi[lgi]
|
||||||
local ctx = pa:context("My Test App")
|
|
||||||
|
|
||||||
local loop = lgi.GLib.MainLoop.new()
|
|
||||||
|
|
||||||
ctx:connect(nil, function(state)
|
|
||||||
if state == 4 then
|
|
||||||
print("Connection is ready")
|
|
||||||
|
|
||||||
ctx:get_sinks(function(sinks)
|
|
||||||
ppretty.dump(sinks)
|
|
||||||
loop:quit()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
loop:run()
|
|
||||||
----
|
|
||||||
|
|
10
bors.toml
Normal file
10
bors.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
status = [
|
||||||
|
"check",
|
||||||
|
"test (5.1)",
|
||||||
|
"test (5.2)",
|
||||||
|
"test (5.3)",
|
||||||
|
"test (luajit)",
|
||||||
|
"rock (5.1)",
|
||||||
|
"rock (5.2)",
|
||||||
|
"rock (5.3)",
|
||||||
|
]
|
|
@ -1,49 +1,13 @@
|
||||||
project = 'lua-libpulse-glib'
|
project = 'lgi-async-extra'
|
||||||
title = 'lua-libpulse-glib'
|
title = 'lgi-async-extra'
|
||||||
description = "Lua bindings for PulseAudio's libpulse, using the GLib Main Loop."
|
description = 'An asynchronous high(er)-level API wrapper for lua-lgi'
|
||||||
|
|
||||||
full_description = [[
|
|
||||||
While libpulse provides different event loop implementations, these bindings focus on the GLib comaptibility only.
|
|
||||||
Therefore, all asynchronous functions need to be executed inside a running GLib Main Loop.
|
|
||||||
For now, this loop has to run on GLib's default main context. Custom loop contexts are currently not supported.
|
|
||||||
|
|
||||||
The majority of the API is callback-based asynchronous. Callbacks always receive any potential errors as their
|
|
||||||
first parameter, usually just the error message as string, or `nil` when there was no error. Additional parameters
|
|
||||||
may either be just a boolean, for operations that don't return data, or the actual queried data.
|
|
||||||
|
|
||||||
All numeric indices (such as sink or source indices) are adjusted to be 1-based in typical Lua fashion.
|
|
||||||
This means that when comparing the output of calls like @{Context:get_sinks} to the output of tools like `pactl`,
|
|
||||||
indices will be off by one.
|
|
||||||
|
|
||||||
local lgi = require("lgi")
|
|
||||||
local pulseaudio = require("lua_libpulse_glib")
|
|
||||||
local ppretty = require("pl.pretty")
|
|
||||||
|
|
||||||
local pa = pulseaudio.new()
|
|
||||||
local ctx = pa:context("My Test App")
|
|
||||||
|
|
||||||
local loop = lgi.GLib.MainLoop.new()
|
|
||||||
|
|
||||||
ctx:connect(nil, function(state)
|
|
||||||
if state == 4 then
|
|
||||||
print("Connection is ready")
|
|
||||||
|
|
||||||
ctx:get_sinks(function(sinks)
|
|
||||||
ppretty.dump(sinks)
|
|
||||||
loop:quit()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
loop:run()
|
|
||||||
]]
|
|
||||||
|
|
||||||
template = true
|
template = true
|
||||||
|
|
||||||
format = 'discount'
|
format = 'discount'
|
||||||
|
|
||||||
pretty = 'lua'
|
pretty = 'lua'
|
||||||
-- prettify_files = 'show'
|
prettify_files = 'show'
|
||||||
backtick_references = false
|
backtick_references = false
|
||||||
wrap = true
|
wrap = true
|
||||||
no_space_before_args = true
|
no_space_before_args = true
|
||||||
|
|
1
lib/lgi
Submodule
1
lib/lgi
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 340a250ab0dfc157fe027f7a29eafda9b8572e5c
|
|
@ -1,18 +1,20 @@
|
||||||
package = "lua-libpulse-glib"
|
package = "lgi-pulseaudio"
|
||||||
version = "scm-1"
|
version = "scm-1"
|
||||||
|
|
||||||
source = {
|
source = {
|
||||||
url = "git://github.com/sclu1034/lua-libpulse-glib.git"
|
url = "git://github.com/sclu1034/lgi-pulseaudio.git"
|
||||||
}
|
}
|
||||||
|
|
||||||
description = {
|
description = {
|
||||||
summary = "Lua bindings for PulseAudio's libpulse, using the GLib Main Loop.",
|
summary = "An asynchronous high(er)-level API wrapper for LGI",
|
||||||
homepage = "https://github.com/sclu1034/lua-libpulse-glib",
|
homepage = "https://github.com/sclu1034/lgi-pulseaudio",
|
||||||
license = "GPLv3"
|
license = "GPLv3"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies = {
|
dependencies = {
|
||||||
"lua >= 5.1"
|
"lua >= 5.1",
|
||||||
|
"lgi",
|
||||||
|
"async.lua"
|
||||||
}
|
}
|
||||||
|
|
||||||
build = {
|
build = {
|
143
src/lgi-pulseaudio/pulseaudio.c
Normal file
143
src/lgi-pulseaudio/pulseaudio.c
Normal file
|
@ -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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <glib-2.0/glib.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <pulse/glib-mainloop.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -1,62 +0,0 @@
|
||||||
#include "callback.h"
|
|
||||||
|
|
||||||
#include "pulseaudio.h"
|
|
||||||
|
|
||||||
|
|
||||||
simple_callback_data* prepare_lua_callback(lua_State* L, int callback_index) {
|
|
||||||
// Prepare a new thread to run the callback with
|
|
||||||
lua_pushstring(L, LUA_PULSEAUDIO);
|
|
||||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
|
||||||
lua_pushstring(L, LUA_PA_REGISTRY);
|
|
||||||
lua_gettable(L, -2);
|
|
||||||
lua_State* thread = lua_newthread(L);
|
|
||||||
int thread_ref = luaL_ref(L, -2);
|
|
||||||
|
|
||||||
if (callback_index != 0) {
|
|
||||||
// Copy the callback function to the thread's stack
|
|
||||||
luaL_checktype(L, callback_index, LUA_TFUNCTION);
|
|
||||||
lua_pushvalue(L, callback_index);
|
|
||||||
lua_xmove(L, thread, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
simple_callback_data* data = malloc(sizeof(struct simple_callback_data));
|
|
||||||
data->L = thread;
|
|
||||||
data->thread_ref = thread_ref;
|
|
||||||
data->is_list = false;
|
|
||||||
|
|
||||||
// Clean up the intermediate data from creating the thread
|
|
||||||
lua_pop(L, 2);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void free_lua_callback(simple_callback_data* data) {
|
|
||||||
lua_State* L = data->L;
|
|
||||||
|
|
||||||
// Remove thread reference
|
|
||||||
lua_pushstring(L, LUA_PULSEAUDIO);
|
|
||||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
|
||||||
lua_pushstring(L, LUA_PA_REGISTRY);
|
|
||||||
lua_gettable(L, -2);
|
|
||||||
luaL_unref(L, -1, data->thread_ref);
|
|
||||||
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When preparing a thread for this callback, the function must be at index `1`.
|
|
||||||
// Values on the stack after that will be ignored. This allows adding userdata and other values
|
|
||||||
// for the purpose of memory management, to keep them alive until the callback has been called.
|
|
||||||
void success_callback(pa_context* c, int success, void* userdata) {
|
|
||||||
simple_callback_data* data = (simple_callback_data*) userdata;
|
|
||||||
lua_State* L = data->L;
|
|
||||||
|
|
||||||
// Copy the callback function to a position from where it can be called.
|
|
||||||
// There may be other values on the stack for memory management.
|
|
||||||
lua_pushvalue(L, 1);
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushboolean(L, success);
|
|
||||||
lua_call(L, 2, 0);
|
|
||||||
|
|
||||||
free_lua_callback(data);
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <lua.h>
|
|
||||||
#include <pulse/context.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
typedef struct simple_callback_data {
|
|
||||||
lua_State* L;
|
|
||||||
int thread_ref;
|
|
||||||
// PulseAudio's introspection callbacks are used for operations that return single values, as well as operations
|
|
||||||
// that return a list. But the callback itself cannot know in which context it is called, so we have to provide
|
|
||||||
// that information explicitly.
|
|
||||||
bool is_list;
|
|
||||||
} simple_callback_data;
|
|
||||||
|
|
||||||
|
|
||||||
// Prepares a Lua thread that can call a Lua function as
|
|
||||||
// callback inside a PulseAudio callback.
|
|
||||||
//
|
|
||||||
// Assumes that the Lua function to call is at the top of the stack and copies it to the
|
|
||||||
// new thread.
|
|
||||||
//
|
|
||||||
// The thread will be kept in memory by a unique ref in the registry, so the callback has to
|
|
||||||
// `luaL_unref` that, to mark it for the garbage collector.
|
|
||||||
//
|
|
||||||
// The returned callback data needs to be `free()`d at the end of the callback. `free_lua_callback`
|
|
||||||
// handles both `free()` and `luaL_unref()`.
|
|
||||||
//
|
|
||||||
// The first `int` is the index to a function on the stack. If this is non-zero, that value will
|
|
||||||
// be copied to the thread's stack.
|
|
||||||
simple_callback_data* prepare_lua_callback(lua_State*, int);
|
|
||||||
|
|
||||||
|
|
||||||
// Removes the thread reference, to allow the thread to be garbage collected, and `free`s
|
|
||||||
// the callback data.
|
|
||||||
void free_lua_callback(simple_callback_data*);
|
|
||||||
|
|
||||||
|
|
||||||
// Simple implementation of `pa_context_success_cb_t` that calls a provided Lua function.
|
|
||||||
void success_callback(pa_context*, int, void*);
|
|
|
@ -1,305 +0,0 @@
|
||||||
#include "context.h"
|
|
||||||
|
|
||||||
#include "lua_util.h"
|
|
||||||
#include "pulseaudio.h"
|
|
||||||
|
|
||||||
#include <pulse/context.h>
|
|
||||||
#include <pulse/error.h>
|
|
||||||
#include <pulse/subscribe.h>
|
|
||||||
|
|
||||||
|
|
||||||
/* Calls the user-provided callback with the updated state info.
|
|
||||||
*/
|
|
||||||
void context_state_callback(pa_context* c, void* userdata) {
|
|
||||||
simple_callback_data* data = (simple_callback_data*) userdata;
|
|
||||||
luaL_checktype(data->L, 1, LUA_TFUNCTION);
|
|
||||||
// `lua_call` will pop the function and arguments from the stack, but this callback will likely be called
|
|
||||||
// multiple times.
|
|
||||||
// To preseve the values for future calls, we need to duplicate them.
|
|
||||||
lua_pushvalue(data->L, 1);
|
|
||||||
// This can't really fail, but for consistency, we keep the error value.
|
|
||||||
lua_pushnil(data->L);
|
|
||||||
|
|
||||||
pa_context_state_t state = pa_context_get_state(c);
|
|
||||||
lua_pushinteger(data->L, state);
|
|
||||||
|
|
||||||
lua_call(data->L, 2, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Calls the user-prodivded event callbacks.
|
|
||||||
*/
|
|
||||||
void context_event_callback(pa_context* c, pa_subscription_event_type_t event_type, uint32_t index, void* userdata) {
|
|
||||||
simple_callback_data* data = (simple_callback_data*) userdata;
|
|
||||||
lua_State* L = data->L;
|
|
||||||
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
luaL_checkudata(L, 2, LUA_PA_CONTEXT);
|
|
||||||
|
|
||||||
// Iterate over the list of subscription callbacks and call each one
|
|
||||||
lua_pushnil(L);
|
|
||||||
while (lua_next(L, 1) != 0) {
|
|
||||||
// TODO: Once we do have the "nothing here" value, we need to check for that here.
|
|
||||||
|
|
||||||
// Copy the `self` parameter
|
|
||||||
lua_pushvalue(L, 2);
|
|
||||||
lua_pushinteger(L, event_type);
|
|
||||||
lua_pushinteger(L, index);
|
|
||||||
|
|
||||||
lua_call(L, 3, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void context_subscribe_success_callback(pa_context* _c, int success, void* userdata) {
|
|
||||||
simple_callback_data* data = (simple_callback_data*) userdata;
|
|
||||||
if (!success) {
|
|
||||||
luaL_error(data->L, "Failed to subscribe to events");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int context_new(lua_State* L, pa_mainloop_api* pa_api) {
|
|
||||||
const char* name = luaL_checkstring(L, -1);
|
|
||||||
// TODO: libpulse recommends using `new_with_proplist` instead. But I need to figure out that `proplist` first.
|
|
||||||
pa_context* ctx = pa_context_new(pa_api, name);
|
|
||||||
if (ctx == NULL) {
|
|
||||||
return luaL_error(L, "failed to create pulseaudio context");
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pa_context* lgi_ctx = lua_newuserdata(L, sizeof(lua_pa_context));
|
|
||||||
if (lgi_ctx == NULL) {
|
|
||||||
return luaL_error(L, "failed to create context userdata");
|
|
||||||
}
|
|
||||||
lgi_ctx->context = ctx;
|
|
||||||
lgi_ctx->connected = FALSE;
|
|
||||||
lgi_ctx->subscribed = FALSE;
|
|
||||||
lgi_ctx->state_callback_data = prepare_lua_callback(L, 0);
|
|
||||||
lgi_ctx->event_callback_data = prepare_lua_callback(L, 0);
|
|
||||||
|
|
||||||
// Create the table used to store the subscription callbacks.
|
|
||||||
lua_newtable(lgi_ctx->event_callback_data->L);
|
|
||||||
|
|
||||||
luaL_getmetatable(L, LUA_PA_CONTEXT);
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
|
|
||||||
// Copy the `context` value to the event callback
|
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_xmove(L, lgi_ctx->event_callback_data->L, 1);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int context__gc(lua_State* L) {
|
|
||||||
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
|
||||||
|
|
||||||
if (ctx->connected == TRUE) {
|
|
||||||
pa_context_disconnect(ctx->context);
|
|
||||||
ctx->connected = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->state_callback_data != NULL) {
|
|
||||||
free_lua_callback(ctx->state_callback_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->event_callback_data != NULL) {
|
|
||||||
free_lua_callback(ctx->event_callback_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
pa_context_unref(ctx->context);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int context_connect(lua_State* L) {
|
|
||||||
int nargs = lua_gettop(L);
|
|
||||||
const char* server = NULL;
|
|
||||||
|
|
||||||
if (lua_type(L, 2) == LUA_TSTRING)
|
|
||||||
server = lua_tostring(L, 2);
|
|
||||||
else if (lua_type(L, 2) != LUA_TNIL) {
|
|
||||||
const char* typearg;
|
|
||||||
if (luaL_getmetafield(L, 2, "__name") == LUA_TSTRING)
|
|
||||||
typearg = lua_tostring(L, -1);
|
|
||||||
else if (lua_type(L, 2) == LUA_TLIGHTUSERDATA)
|
|
||||||
typearg = "light userdata";
|
|
||||||
else
|
|
||||||
typearg = luaL_typename(L, 2);
|
|
||||||
|
|
||||||
return luaL_argerror(L, 2, lua_pushfstring(L, "string or nil expected, got %s", typearg));
|
|
||||||
}
|
|
||||||
|
|
||||||
luaL_checktype(L, 3, LUA_TFUNCTION);
|
|
||||||
|
|
||||||
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
|
||||||
|
|
||||||
pa_context_flags_t flags = PA_CONTEXT_NOAUTOSPAWN;
|
|
||||||
if (nargs > 3)
|
|
||||||
flags = luaL_checkinteger(L, 4);
|
|
||||||
|
|
||||||
// Make sure the callback function is at a known position in the thread's stack
|
|
||||||
lua_settop(ctx->state_callback_data->L, 0);
|
|
||||||
lua_pushvalue(L, 3);
|
|
||||||
lua_xmove(L, ctx->state_callback_data->L, 1);
|
|
||||||
|
|
||||||
pa_context_set_state_callback(ctx->context, context_state_callback, ctx->state_callback_data);
|
|
||||||
pa_context_set_subscribe_callback(ctx->context, context_event_callback, ctx->event_callback_data);
|
|
||||||
|
|
||||||
// TODO: Check if I need to create bindings for `pa_spawn_api`.
|
|
||||||
int ret = pa_context_connect(ctx->context, server, flags, NULL);
|
|
||||||
if (ret < 0) {
|
|
||||||
return luaL_error(L, "failed to connect: %s", pa_strerror(ret));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int context_disconnect(lua_State* L) {
|
|
||||||
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
|
||||||
pa_context_disconnect(ctx->context);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int context_get_state(lua_State* L) {
|
|
||||||
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
|
||||||
pa_context_state_t state = pa_context_get_state(ctx->context);
|
|
||||||
lua_pushinteger(L, state);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int context_set_default_sink(lua_State* L) {
|
|
||||||
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
|
||||||
const char* name = luaL_checkstring(L, 2);
|
|
||||||
|
|
||||||
simple_callback_data* data = prepare_lua_callback(L, 3);
|
|
||||||
|
|
||||||
pa_operation* op = pa_context_set_default_sink(ctx->context, name, success_callback, data);
|
|
||||||
if (op == NULL) {
|
|
||||||
int error = pa_context_errno(ctx->context);
|
|
||||||
lua_pushvalue(L, 2);
|
|
||||||
lua_pushfstring(L, "failed to set default sink: %s", pa_strerror(error));
|
|
||||||
lua_call(L, 1, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int context_set_default_source(lua_State* L) {
|
|
||||||
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
|
||||||
const char* name = luaL_checkstring(L, 2);
|
|
||||||
|
|
||||||
simple_callback_data* data = prepare_lua_callback(L, 3);
|
|
||||||
|
|
||||||
pa_operation* op = pa_context_set_default_source(ctx->context, name, success_callback, data);
|
|
||||||
if (op == NULL) {
|
|
||||||
int error = pa_context_errno(ctx->context);
|
|
||||||
lua_pushvalue(L, 2);
|
|
||||||
lua_pushfstring(L, "failed to set default source: %s", pa_strerror(error));
|
|
||||||
lua_call(L, 1, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int context_subscribe(lua_State* L) {
|
|
||||||
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
|
||||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
|
||||||
|
|
||||||
// This call is only effective when the connection state is "ready".
|
|
||||||
// So we have to do it here, rather than during `context_connect`,
|
|
||||||
// but we also need to make sure it's only called once.
|
|
||||||
if (!ctx->subscribed) {
|
|
||||||
pa_context_subscribe(ctx->context,
|
|
||||||
PA_SUBSCRIPTION_MASK_ALL,
|
|
||||||
context_subscribe_success_callback,
|
|
||||||
ctx->event_callback_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pos = lua_rawlen(ctx->event_callback_data->L, 1) + 1;
|
|
||||||
// Duplicate the callback function, so we can move it over to the other thread
|
|
||||||
// TODO: Do we actually need to duplicate?
|
|
||||||
lua_pushvalue(L, 2);
|
|
||||||
lua_xmove(L, ctx->event_callback_data->L, 1);
|
|
||||||
lua_rawseti(ctx->event_callback_data->L, 1, pos);
|
|
||||||
|
|
||||||
lua_pushinteger(L, pos);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int context_unsubscribe(lua_State* L) {
|
|
||||||
lua_pa_context* ctx = luaL_checkudata(L, 1, LUA_PA_CONTEXT);
|
|
||||||
lua_State* thread_L = ctx->event_callback_data->L;
|
|
||||||
size_t pos = 0;
|
|
||||||
size_t len = lua_rawlen(thread_L, 1);
|
|
||||||
|
|
||||||
if (len == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Handle calling this twice on the same index.
|
|
||||||
// Given that we use an array to track things, and Lua's way of counting in arrays
|
|
||||||
// doesn't like `nil`s, we probably need a special "nothing here" value that's not `nil`,
|
|
||||||
// and signifies an index that has already been unsubscribed.
|
|
||||||
|
|
||||||
// TODO: Simplify things. Supporting just the index is enough.
|
|
||||||
// Comparing by function is convenient, but also confusing to inexperienced devs.
|
|
||||||
|
|
||||||
switch (lua_type(L, 2)) {
|
|
||||||
case LUA_TNUMBER: {
|
|
||||||
pos = lua_tointeger(L, 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LUA_TFUNCTION: {
|
|
||||||
bool found = false;
|
|
||||||
size_t i = 0;
|
|
||||||
|
|
||||||
// Duplicate the function value, so we can move it other to the other thread for comparing
|
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_xmove(L, thread_L, 1);
|
|
||||||
int fn_index = lua_gettop(thread_L);
|
|
||||||
|
|
||||||
lua_pushnil(L);
|
|
||||||
while (lua_next(thread_L, 1) != 0) {
|
|
||||||
++i;
|
|
||||||
|
|
||||||
if (lua_equal(thread_L, -1, fn_index) == 1) {
|
|
||||||
pos = i;
|
|
||||||
lua_pop(thread_L, 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the value, but keep the key to continue iterating
|
|
||||||
lua_pop(thread_L, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
return luaL_error(L, "couldn't find this function in the list of subscriptions");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return luaL_argerror(L, 2, "expected number or function");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: As explained above, we need to handle calling unsubscribe twice better.
|
|
||||||
// Indices should not be re-used but replaced by a special "nothing here" value.
|
|
||||||
for (; pos < len; ++pos) {
|
|
||||||
lua_rawgeti(thread_L, 1, pos + 1);
|
|
||||||
lua_rawseti(thread_L, 1, pos);
|
|
||||||
}
|
|
||||||
lua_pushnil(thread_L);
|
|
||||||
lua_rawseti(thread_L, 1, len);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,536 +0,0 @@
|
||||||
/** Bindings for libpulse's connection context.
|
|
||||||
*
|
|
||||||
* The connection @{Context} provides introspection calls to query state from the server and various commands to
|
|
||||||
* change this state.
|
|
||||||
*
|
|
||||||
* In many cases, sinks and sources may be addressed by either their name or their numeric index.
|
|
||||||
* Both can be queried using the `get_(sink|source)_info` or `get_(sink|source)s` calls.
|
|
||||||
*
|
|
||||||
* @module lua_libpulse_glib.context
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "callback.h"
|
|
||||||
|
|
||||||
#include <lauxlib.h>
|
|
||||||
#include <lua.h>
|
|
||||||
#include <pulse/context.h>
|
|
||||||
#include <pulse/mainloop-api.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#define LUA_PA_CONTEXT "pulseaudio.context"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct lua_pa_context {
|
|
||||||
pa_context* context;
|
|
||||||
bool connected;
|
|
||||||
bool subscribed;
|
|
||||||
simple_callback_data* state_callback_data;
|
|
||||||
simple_callback_data* event_callback_data;
|
|
||||||
} lua_pa_context;
|
|
||||||
|
|
||||||
|
|
||||||
int context_new(lua_State*, pa_mainloop_api*);
|
|
||||||
int context__gc(lua_State*);
|
|
||||||
|
|
||||||
/// Callback Functions
|
|
||||||
/// @section callbacks
|
|
||||||
|
|
||||||
/** The callback signature for @{Context:subscribe}.
|
|
||||||
*
|
|
||||||
* See [here](https://freedesktop.org/software/pulseaudio/doxygen/subscribe.html) how to use the `event_type`
|
|
||||||
* parameter to determine the event that triggered the callback.
|
|
||||||
*
|
|
||||||
* @function event_callback
|
|
||||||
* @tparam Context context The context that the callback is subscribed to.
|
|
||||||
* @tparam number event_type A bitflag-ish value from libpulse that represents the event that caused the callback.
|
|
||||||
* @tparam number index
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** The callback signature for @{Context:connect}.
|
|
||||||
*
|
|
||||||
* @function state_callback
|
|
||||||
* @tparam Context context The context that the callback is subscribed to.
|
|
||||||
* @tparam number state An enum that represents the current connection state.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/// Context
|
|
||||||
/// @type Context
|
|
||||||
|
|
||||||
|
|
||||||
/** Connects the context to the given server address.
|
|
||||||
*
|
|
||||||
* If the server address is `nil`, libpulse will attempt to connect to what it considers the default server.
|
|
||||||
* In most cases, this is the local machine.
|
|
||||||
*
|
|
||||||
* The provided callback function will be registered as state callback function, and will be called whenever the
|
|
||||||
* context's connection state changes.
|
|
||||||
*
|
|
||||||
* @function Context:connect
|
|
||||||
* @async
|
|
||||||
* @tparam[opt=nil] string server_address The server address.
|
|
||||||
* @tparam function cb The connection state callback.
|
|
||||||
* @treturn[opt] string The error message
|
|
||||||
* @treturn string The state
|
|
||||||
*/
|
|
||||||
int context_connect(lua_State*);
|
|
||||||
|
|
||||||
/** Disconnects from the server.
|
|
||||||
*
|
|
||||||
* @function Context:disconnect
|
|
||||||
*/
|
|
||||||
int context_disconnect(lua_State*);
|
|
||||||
|
|
||||||
/** Returns the current connection state.
|
|
||||||
*
|
|
||||||
* @function Context:get_state
|
|
||||||
* @treturn number
|
|
||||||
*/
|
|
||||||
int context_get_state(lua_State*);
|
|
||||||
|
|
||||||
/** Registers a callback function as event handler.
|
|
||||||
*
|
|
||||||
* This callback will be called whenever the server sends a suitable event.
|
|
||||||
*
|
|
||||||
* Any number of callbacks may be registered at the same time, and can be unscubscribed with
|
|
||||||
* @{Context:unsubscribe}, using the returned subscription ID.
|
|
||||||
*
|
|
||||||
* This must called after the connection state changed to "ready" (index `4`). Otherwise
|
|
||||||
* it will have no effect.
|
|
||||||
*
|
|
||||||
* @function Context:subscribe
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn number The subscription ID.
|
|
||||||
*/
|
|
||||||
int context_subscribe(lua_State*);
|
|
||||||
|
|
||||||
/** Removes an event handler subscription.
|
|
||||||
*
|
|
||||||
* Subscriptions may be removed either by their ID or by their callback function.
|
|
||||||
*
|
|
||||||
* @function Context:unsubscribe
|
|
||||||
* @tparam number|function handler The handler to remove.
|
|
||||||
*/
|
|
||||||
int context_unsubscribe(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
/** Sets the default sink.
|
|
||||||
*
|
|
||||||
* @function Context:set_default_sink
|
|
||||||
* @async
|
|
||||||
* @tparam string sink Name of the sink to set as default.
|
|
||||||
* @treturn[opt] string error
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_default_sink(lua_State*);
|
|
||||||
|
|
||||||
/** Sets the default source.
|
|
||||||
*
|
|
||||||
* @function Context:set_default_source
|
|
||||||
* @async
|
|
||||||
* @tparam string source Name of the source to set as default.
|
|
||||||
* @treturn[opt] string error
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_default_source(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
/** Gets information about the server the context is connected to.
|
|
||||||
*
|
|
||||||
* See [pa_server_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__server__info.html)
|
|
||||||
* for documentation on the return type.
|
|
||||||
*
|
|
||||||
* This will fail when the connection state is anything other than `READY`.
|
|
||||||
*
|
|
||||||
* @function Context:get_server_info
|
|
||||||
* @async
|
|
||||||
* @tparam function cb
|
|
||||||
* @return[opt] string The error
|
|
||||||
* @return table The server info.
|
|
||||||
*/
|
|
||||||
int context_get_server_info(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
// Sinks
|
|
||||||
|
|
||||||
|
|
||||||
/** Gets information about the given sink.
|
|
||||||
*
|
|
||||||
* The sink may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* See [pa_sink_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__sink__info.html)
|
|
||||||
* for documentation on the return type.
|
|
||||||
*
|
|
||||||
* @function Context:get_sink_info
|
|
||||||
* @async
|
|
||||||
* @tparam number|string sink The index or name of the sink to query.
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn table
|
|
||||||
*/
|
|
||||||
int context_get_sink_info(lua_State*);
|
|
||||||
|
|
||||||
/** Gets information about all sinks.
|
|
||||||
*
|
|
||||||
* This returns the same information as @{Context:get_sink_info} would have returned for every sink
|
|
||||||
* that's currently registered at the server.
|
|
||||||
*
|
|
||||||
* See [pa_sink_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__sink__info.html)
|
|
||||||
* for documentation on the return type.
|
|
||||||
*
|
|
||||||
* @function Context:get_sinks
|
|
||||||
* @async
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn table
|
|
||||||
*/
|
|
||||||
int context_get_sink_info_list(lua_State*);
|
|
||||||
int context_get_sink_info_by_name(lua_State*);
|
|
||||||
int context_get_sink_info_by_index(lua_State*);
|
|
||||||
|
|
||||||
/** Sets the sink's volume to the given value.
|
|
||||||
*
|
|
||||||
* The sink may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* @function Context:set_sink_volume
|
|
||||||
* @async
|
|
||||||
* @tparam number|string sink The sink to update.
|
|
||||||
* @tparam Volume volume
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_sink_volume(lua_State*);
|
|
||||||
int context_set_sink_volume_by_name(lua_State*);
|
|
||||||
int context_set_sink_volume_by_index(lua_State*);
|
|
||||||
|
|
||||||
/** Sets the sink's mute state.
|
|
||||||
*
|
|
||||||
* The sink may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* @function Context:set_sink_mute
|
|
||||||
* @async
|
|
||||||
* @tparam number|string sink The sink to update.
|
|
||||||
* @tparam boolean mute
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_sink_mute(lua_State*);
|
|
||||||
int context_set_sink_mute_by_name(lua_State*);
|
|
||||||
int context_set_sink_mute_by_index(lua_State*);
|
|
||||||
|
|
||||||
/** Sets the sink's suspended state.
|
|
||||||
*
|
|
||||||
* The sink may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* @function Context:set_sink_suspended
|
|
||||||
* @async
|
|
||||||
* @tparam number|string sink The sink to update.
|
|
||||||
* @tparam boolean suspended
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_sink_suspended(lua_State*);
|
|
||||||
int context_set_sink_suspended_by_name(lua_State*);
|
|
||||||
int context_set_sink_suspended_by_index(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
// Sink Inputs
|
|
||||||
|
|
||||||
/** Gets information about the given sink input.
|
|
||||||
*
|
|
||||||
* The sink input may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* See [pa_sink_input_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__sink_input__info.html)
|
|
||||||
* for documentation on the return type.
|
|
||||||
*
|
|
||||||
* @function Context:get_sink_input_info
|
|
||||||
* @async
|
|
||||||
* @tparam number|string sink input The index or name of the sink input to query.
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn table
|
|
||||||
*/
|
|
||||||
int context_get_sink_input_info(lua_State*);
|
|
||||||
|
|
||||||
/** Gets information about all sink inputs.
|
|
||||||
*
|
|
||||||
* This returns the same information as @{Context:get_sink_input_info} would have returned for every sink input
|
|
||||||
* that's currently registered at the server.
|
|
||||||
*
|
|
||||||
* See [pa_sink_input_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__sink_input__info.html)
|
|
||||||
* for documentation on the return type.
|
|
||||||
*
|
|
||||||
* @function Context:get_sink_inputs
|
|
||||||
* @async
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn table
|
|
||||||
*/
|
|
||||||
int context_get_sink_input_info_list(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
/** Moves the sink input to a different name.
|
|
||||||
*
|
|
||||||
* The target sink may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* @function Context:set_sink_suspended
|
|
||||||
* @async
|
|
||||||
* @tparam number The sink input to move.
|
|
||||||
* @tparam number|string sink The sink to update.
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_move_sink_input(lua_State*);
|
|
||||||
int context_move_sink_input_by_name(lua_State*);
|
|
||||||
int context_move_sink_input_by_index(lua_State*);
|
|
||||||
|
|
||||||
/** Sets the sink input's volume to the given value.
|
|
||||||
*
|
|
||||||
* @function Context:set_sink_input_volume
|
|
||||||
* @async
|
|
||||||
* @tparam number sink_input The sink input to update.
|
|
||||||
* @tparam Volume volume
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_sink_input_volume(lua_State*);
|
|
||||||
|
|
||||||
/** Sets the sink input's mute state.
|
|
||||||
*
|
|
||||||
* @function Context:set_sink_input_mute
|
|
||||||
* @async
|
|
||||||
* @tparam number sink_input The sink input to update.
|
|
||||||
* @tparam boolean mute
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_sink_input_mute(lua_State*);
|
|
||||||
|
|
||||||
/** Kills the sink input.
|
|
||||||
*
|
|
||||||
* @function Context:kill_sink_input
|
|
||||||
* @async
|
|
||||||
* @tparam number sink_input The sink input to kill.
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_kill_sink_input(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
// Sources
|
|
||||||
|
|
||||||
/** Gets information about the given source.
|
|
||||||
*
|
|
||||||
* The source may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* See [pa_source_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__source__info.html)
|
|
||||||
* for documentation on the return type.
|
|
||||||
*
|
|
||||||
* @function Context:get_source_info
|
|
||||||
* @async
|
|
||||||
* @tparam number|string source The index or name of the source to query.
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn table
|
|
||||||
*/
|
|
||||||
int context_get_source_info(lua_State*);
|
|
||||||
|
|
||||||
/** Gets information about all sources.
|
|
||||||
*
|
|
||||||
* This returns the same information as @{Context:get_source_info} would have returned for every source
|
|
||||||
* that's currently registered at the server.
|
|
||||||
*
|
|
||||||
* See [pa_source_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__source__info.html)
|
|
||||||
* for documentation on the return type.
|
|
||||||
*
|
|
||||||
* @function Context:get_sources
|
|
||||||
* @async
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn table
|
|
||||||
*/
|
|
||||||
int context_get_source_info_list(lua_State*);
|
|
||||||
int context_get_source_info_by_name(lua_State*);
|
|
||||||
int context_get_source_info_by_index(lua_State*);
|
|
||||||
|
|
||||||
/** Sets the source's volume to the given value.
|
|
||||||
*
|
|
||||||
* The source may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* @function Context:set_source_volume
|
|
||||||
* @async
|
|
||||||
* @tparam number|string source The source to update.
|
|
||||||
* @tparam Volume volume
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_source_volume(lua_State*);
|
|
||||||
int context_set_source_volume_by_name(lua_State*);
|
|
||||||
int context_set_source_volume_by_index(lua_State*);
|
|
||||||
|
|
||||||
/** Sets the source's mute state.
|
|
||||||
*
|
|
||||||
* The source may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* @function Context:set_source_mute
|
|
||||||
* @async
|
|
||||||
* @tparam number|string source The source to update.
|
|
||||||
* @tparam boolean mute
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_source_mute(lua_State*);
|
|
||||||
int context_set_source_mute_by_name(lua_State*);
|
|
||||||
int context_set_source_mute_by_index(lua_State*);
|
|
||||||
|
|
||||||
/** Sets the source's suspended state.
|
|
||||||
*
|
|
||||||
* The source may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* @function Context:set_source_suspended
|
|
||||||
* @async
|
|
||||||
* @tparam number|string source The source to update.
|
|
||||||
* @tparam boolean suspended
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_source_suspended(lua_State*);
|
|
||||||
int context_set_source_suspended_by_name(lua_State*);
|
|
||||||
int context_set_source_suspended_by_index(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
// Source Outputs
|
|
||||||
|
|
||||||
/** Gets information about the given source output.
|
|
||||||
*
|
|
||||||
* See [pa_source_output_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__source_output__info.html)
|
|
||||||
* for documentation on the return type.
|
|
||||||
*
|
|
||||||
* @function Context:get_source_output_info
|
|
||||||
* @async
|
|
||||||
* @tparam number source_output The index of the source output to query.
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn table
|
|
||||||
*/
|
|
||||||
int context_get_source_output_info(lua_State*);
|
|
||||||
|
|
||||||
/** Gets information about all source outputs.
|
|
||||||
*
|
|
||||||
* This returns the same information as @{Context:get_source_output_info} would have returned for every source output
|
|
||||||
* that's currently registered at the server.
|
|
||||||
*
|
|
||||||
* See [pa_source_output_info](https://freedesktop.org/software/pulseaudio/doxygen/structpa__source_output__info.html)
|
|
||||||
* for documentation on the return type.
|
|
||||||
*
|
|
||||||
* @function Context:get_source_outputs
|
|
||||||
* @async
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn table
|
|
||||||
*/
|
|
||||||
int context_get_source_output_info_list(lua_State*);
|
|
||||||
|
|
||||||
/** Moves the source output to a different name.
|
|
||||||
*
|
|
||||||
* The target source may be indicated by either its name or its index.
|
|
||||||
*
|
|
||||||
* @function Context:set_source_suspended
|
|
||||||
* @async
|
|
||||||
* @tparam number The source output to move.
|
|
||||||
* @tparam number|string source The source to update.
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_move_source_output(lua_State*);
|
|
||||||
int context_move_source_output_by_name(lua_State*);
|
|
||||||
int context_move_source_output_by_index(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
/** Sets the source output's volume to the given value.
|
|
||||||
*
|
|
||||||
* @function Context:set_source_output_volume
|
|
||||||
* @async
|
|
||||||
* @tparam number source_output The source output to update.
|
|
||||||
* @tparam Volume volume
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_source_output_volume(lua_State*);
|
|
||||||
|
|
||||||
/** Sets the source output's mute state.
|
|
||||||
*
|
|
||||||
* @function Context:set_source_output_mute
|
|
||||||
* @async
|
|
||||||
* @tparam number source_output The source output to update.
|
|
||||||
* @tparam boolean mute
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_set_source_output_mute(lua_State*);
|
|
||||||
|
|
||||||
/** Kills the source output.
|
|
||||||
*
|
|
||||||
* @function Context:kill_source_output
|
|
||||||
* @async
|
|
||||||
* @tparam number source_output The source output to kill.
|
|
||||||
* @tparam function cb
|
|
||||||
* @treturn[opt] string
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int context_kill_source_output(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg context_mt[] = {
|
|
||||||
{"__gc", context__gc},
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg context_f[] = {
|
|
||||||
{"connect", context_connect },
|
|
||||||
{ "disconnect", context_disconnect },
|
|
||||||
{ "subscribe", context_subscribe },
|
|
||||||
{ "unsubscribe", context_unsubscribe },
|
|
||||||
{ "get_state", context_get_state },
|
|
||||||
{ "set_default_sink", context_set_default_sink },
|
|
||||||
{ "set_default_source", context_set_default_source },
|
|
||||||
{ "get_server_info", context_get_server_info },
|
|
||||||
{ "get_sinks", context_get_sink_info_list },
|
|
||||||
{ "get_sink_info", context_get_sink_info },
|
|
||||||
{ "set_sink_volume", context_set_sink_volume },
|
|
||||||
{ "set_sink_mute", context_set_sink_mute },
|
|
||||||
{ "set_sink_suspended", context_set_sink_suspended },
|
|
||||||
{ "get_sink_inputs", context_get_sink_input_info_list },
|
|
||||||
{ "get_sink_input_info", context_get_sink_input_info },
|
|
||||||
{ "set_sink_input_volume", context_set_sink_input_volume },
|
|
||||||
{ "set_sink_input_mute", context_set_sink_input_mute },
|
|
||||||
{ "move_sink_input", context_move_sink_input },
|
|
||||||
{ "kill_sink_input", context_kill_sink_input },
|
|
||||||
{ "set_source_volume", context_set_source_volume },
|
|
||||||
{ "set_source_mute", context_set_source_mute },
|
|
||||||
{ "set_source_suspended", context_set_source_suspended },
|
|
||||||
{ "get_sources", context_get_source_info_list },
|
|
||||||
{ "get_source_info", context_get_source_info },
|
|
||||||
{ "get_source_outputs", context_get_source_output_info_list},
|
|
||||||
{ "get_source_output_info", context_get_source_output_info },
|
|
||||||
{ "set_source_output_volume", context_set_source_output_volume },
|
|
||||||
{ "set_source_output_mute", context_set_source_output_mute },
|
|
||||||
{ "move_source_output", context_move_source_output },
|
|
||||||
{ "kill_source_output", context_kill_source_output },
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
|
@ -1,532 +0,0 @@
|
||||||
#include "convert.h"
|
|
||||||
|
|
||||||
#include "proplist.h"
|
|
||||||
#include "volume.h"
|
|
||||||
|
|
||||||
#include <pulse/xmalloc.h>
|
|
||||||
|
|
||||||
|
|
||||||
void channel_map_to_lua(lua_State* L, const pa_channel_map* spec) {
|
|
||||||
lua_createtable(L, spec->channels, 0);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
for (int i = 0; i < spec->channels; ++i) {
|
|
||||||
lua_pushinteger(L, i + 1);
|
|
||||||
lua_pushinteger(L, spec->map[i]);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void sample_spec_to_lua(lua_State* L, const pa_sample_spec* spec) {
|
|
||||||
lua_createtable(L, 0, 3);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
lua_pushstring(L, "rate");
|
|
||||||
lua_pushinteger(L, spec->rate);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "channels");
|
|
||||||
lua_pushinteger(L, spec->channels);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "format");
|
|
||||||
lua_pushinteger(L, spec->format);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void sink_port_info_to_lua(lua_State* L, const pa_sink_port_info* info) {
|
|
||||||
lua_createtable(L, 0, 6);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
lua_pushstring(L, "name");
|
|
||||||
lua_pushstring(L, info->name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "description");
|
|
||||||
lua_pushstring(L, info->description);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "priority");
|
|
||||||
lua_pushinteger(L, info->priority);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "available");
|
|
||||||
lua_pushinteger(L, info->available);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "availability_group");
|
|
||||||
lua_pushstring(L, info->availability_group);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "type");
|
|
||||||
lua_pushinteger(L, info->type);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void source_port_info_to_lua(lua_State* L, const pa_source_port_info* info) {
|
|
||||||
lua_createtable(L, 0, 6);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
lua_pushstring(L, "name");
|
|
||||||
lua_pushstring(L, info->name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "description");
|
|
||||||
lua_pushstring(L, info->description);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "priority");
|
|
||||||
lua_pushinteger(L, info->priority);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "available");
|
|
||||||
lua_pushinteger(L, info->available);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "availability_group");
|
|
||||||
lua_pushstring(L, info->availability_group);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "type");
|
|
||||||
lua_pushinteger(L, info->type);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void format_info_to_lua(lua_State* L, const pa_format_info* info) {
|
|
||||||
lua_createtable(L, 0, 2);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
lua_pushstring(L, "encoding");
|
|
||||||
lua_pushinteger(L, info->encoding);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "plist");
|
|
||||||
proplist_to_lua(L, pa_proplist_copy(info->plist));
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void sink_ports_to_lua(lua_State* L, pa_sink_port_info** list, int n_ports, pa_sink_port_info* active) {
|
|
||||||
lua_createtable(L, n_ports, 1);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
for (int i = 0; i < n_ports; ++i) {
|
|
||||||
lua_pushinteger(L, i + 1);
|
|
||||||
sink_port_info_to_lua(L, list[i]);
|
|
||||||
|
|
||||||
if (list[i] == active) {
|
|
||||||
lua_pushstring(L, "active");
|
|
||||||
lua_pushvalue(L, -2);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void source_ports_to_lua(lua_State* L, pa_source_port_info** list, int n_ports, pa_source_port_info* active) {
|
|
||||||
lua_createtable(L, n_ports, 1);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
for (int i = 0; i < n_ports; ++i) {
|
|
||||||
lua_pushinteger(L, i + 1);
|
|
||||||
source_port_info_to_lua(L, list[i]);
|
|
||||||
|
|
||||||
if (list[i] == active) {
|
|
||||||
lua_pushstring(L, "active");
|
|
||||||
lua_pushvalue(L, -2);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void formats_to_lua(lua_State* L, pa_format_info** list, int n_formats) {
|
|
||||||
lua_createtable(L, n_formats, 0);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
for (int i = 0; i < n_formats; ++i) {
|
|
||||||
lua_pushinteger(L, i + 1);
|
|
||||||
format_info_to_lua(L, list[i]);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void sink_info_to_lua(lua_State* L, const pa_sink_info* info) {
|
|
||||||
lua_createtable(L, 0, 24);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
lua_pushstring(L, "name");
|
|
||||||
lua_pushstring(L, info->name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "index");
|
|
||||||
// Convert C's 0-based index to Lua's 1-base
|
|
||||||
lua_pushinteger(L, info->index + 1);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "description");
|
|
||||||
lua_pushstring(L, info->description);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "sample_spec");
|
|
||||||
sample_spec_to_lua(L, &info->sample_spec);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "channel_map");
|
|
||||||
channel_map_to_lua(L, &info->channel_map);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "owner_module");
|
|
||||||
lua_pushinteger(L, info->owner_module);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "volume");
|
|
||||||
volume_to_lua(L, &info->volume);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "mute");
|
|
||||||
lua_pushinteger(L, info->mute);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "monitor_source");
|
|
||||||
lua_pushinteger(L, info->monitor_source);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "monitor_source_name");
|
|
||||||
lua_pushstring(L, info->monitor_source_name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "latency");
|
|
||||||
lua_pushinteger(L, info->latency);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "driver");
|
|
||||||
lua_pushstring(L, info->driver);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "flags");
|
|
||||||
lua_pushinteger(L, info->flags);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "proplist");
|
|
||||||
proplist_to_lua(L, pa_proplist_copy(info->proplist));
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "base_volume");
|
|
||||||
lua_pushinteger(L, info->base_volume);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "state");
|
|
||||||
lua_pushinteger(L, info->state);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "n_volume_steps");
|
|
||||||
lua_pushinteger(L, info->n_volume_steps);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "card");
|
|
||||||
lua_pushinteger(L, info->card);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "ports");
|
|
||||||
sink_ports_to_lua(L, info->ports, info->n_ports, info->active_port);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "formats");
|
|
||||||
formats_to_lua(L, info->formats, info->n_formats);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void source_info_to_lua(lua_State* L, const pa_source_info* info) {
|
|
||||||
lua_createtable(L, 0, 24);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
lua_pushstring(L, "name");
|
|
||||||
lua_pushstring(L, info->name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "index");
|
|
||||||
// Convert C's 0-based index to Lua's 1-base
|
|
||||||
lua_pushinteger(L, info->index + 1);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "description");
|
|
||||||
lua_pushstring(L, info->description);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "sample_spec");
|
|
||||||
sample_spec_to_lua(L, &info->sample_spec);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "channel_map");
|
|
||||||
channel_map_to_lua(L, &info->channel_map);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "owner_module");
|
|
||||||
lua_pushinteger(L, info->owner_module);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "volume");
|
|
||||||
volume_to_lua(L, &info->volume);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "mute");
|
|
||||||
lua_pushinteger(L, info->mute);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "monitor_of_sink");
|
|
||||||
lua_pushinteger(L, info->monitor_of_sink);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "monitor_of_sink_name");
|
|
||||||
lua_pushstring(L, info->monitor_of_sink_name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "latency");
|
|
||||||
lua_pushinteger(L, info->latency);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "driver");
|
|
||||||
lua_pushstring(L, info->driver);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "flags");
|
|
||||||
lua_pushinteger(L, info->flags);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "proplist");
|
|
||||||
proplist_to_lua(L, pa_proplist_copy(info->proplist));
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "configured_latency");
|
|
||||||
lua_pushinteger(L, info->configured_latency);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "base_volume");
|
|
||||||
lua_pushinteger(L, info->base_volume);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "state");
|
|
||||||
lua_pushinteger(L, info->state);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "n_volume_steps");
|
|
||||||
lua_pushinteger(L, info->n_volume_steps);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "card");
|
|
||||||
lua_pushinteger(L, info->card);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "ports");
|
|
||||||
source_ports_to_lua(L, info->ports, info->n_ports, info->active_port);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "formats");
|
|
||||||
formats_to_lua(L, info->formats, info->n_formats);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void server_info_to_lua(lua_State* L, const pa_server_info* info) {
|
|
||||||
lua_createtable(L, 0, 9);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
lua_pushstring(L, "user_name");
|
|
||||||
lua_pushstring(L, info->user_name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "host_name");
|
|
||||||
lua_pushstring(L, info->host_name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "server_version");
|
|
||||||
lua_pushstring(L, info->server_version);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "server_name");
|
|
||||||
lua_pushstring(L, info->server_name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "default_sink_name");
|
|
||||||
lua_pushstring(L, info->default_sink_name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "default_source_name");
|
|
||||||
lua_pushstring(L, info->default_source_name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "cookie");
|
|
||||||
lua_pushinteger(L, info->cookie);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "sample_spec");
|
|
||||||
sample_spec_to_lua(L, &info->sample_spec);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "channel_map");
|
|
||||||
channel_map_to_lua(L, &info->channel_map);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void sink_input_info_to_lua(lua_State* L, const pa_sink_input_info* info) {
|
|
||||||
lua_createtable(L, 0, 15);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
lua_pushstring(L, "index");
|
|
||||||
lua_pushinteger(L, info->index + 1);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "name");
|
|
||||||
lua_pushstring(L, info->name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "owner_module");
|
|
||||||
lua_pushinteger(L, info->owner_module);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "client");
|
|
||||||
lua_pushinteger(L, info->client);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "sink");
|
|
||||||
lua_pushinteger(L, info->sink);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "sample_spec");
|
|
||||||
sample_spec_to_lua(L, &info->sample_spec);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "channel_map");
|
|
||||||
channel_map_to_lua(L, &info->channel_map);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "has_volume");
|
|
||||||
lua_pushboolean(L, info->has_volume);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "volume_writable");
|
|
||||||
lua_pushboolean(L, info->volume_writable);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
if (info->has_volume) {
|
|
||||||
lua_pushstring(L, "volume");
|
|
||||||
volume_to_lua(L, &info->volume);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushstring(L, "buffer_usec");
|
|
||||||
lua_pushinteger(L, info->buffer_usec);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "sink_usec");
|
|
||||||
lua_pushinteger(L, info->sink_usec);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "resample_method");
|
|
||||||
lua_pushstring(L, info->resample_method);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "driver");
|
|
||||||
lua_pushstring(L, info->driver);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "mute");
|
|
||||||
lua_pushboolean(L, info->mute);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "format");
|
|
||||||
format_info_to_lua(L, info->format);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "proplist");
|
|
||||||
proplist_to_lua(L, pa_proplist_copy(info->proplist));
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void source_output_info_to_lua(lua_State* L, const pa_source_output_info* info) {
|
|
||||||
lua_createtable(L, 0, 15);
|
|
||||||
int table_index = lua_gettop(L);
|
|
||||||
|
|
||||||
lua_pushstring(L, "index");
|
|
||||||
lua_pushinteger(L, info->index + 1);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "name");
|
|
||||||
lua_pushstring(L, info->name);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "owner_module");
|
|
||||||
lua_pushinteger(L, info->owner_module);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "client");
|
|
||||||
lua_pushinteger(L, info->client);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "source");
|
|
||||||
lua_pushinteger(L, info->source);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "sample_spec");
|
|
||||||
sample_spec_to_lua(L, &info->sample_spec);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "channel_map");
|
|
||||||
channel_map_to_lua(L, &info->channel_map);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "has_volume");
|
|
||||||
lua_pushboolean(L, info->has_volume);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "volume_writable");
|
|
||||||
lua_pushboolean(L, info->volume_writable);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
if (info->has_volume) {
|
|
||||||
lua_pushstring(L, "volume");
|
|
||||||
volume_to_lua(L, &info->volume);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushstring(L, "buffer_usec");
|
|
||||||
lua_pushinteger(L, info->buffer_usec);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "source_usec");
|
|
||||||
lua_pushinteger(L, info->source_usec);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "resample_method");
|
|
||||||
lua_pushstring(L, info->resample_method);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "driver");
|
|
||||||
lua_pushstring(L, info->driver);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "mute");
|
|
||||||
lua_pushboolean(L, info->mute);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "format");
|
|
||||||
format_info_to_lua(L, info->format);
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
|
|
||||||
lua_pushstring(L, "proplist");
|
|
||||||
proplist_to_lua(L, pa_proplist_copy(info->proplist));
|
|
||||||
lua_settable(L, table_index);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <lua.h>
|
|
||||||
#include <pulse/error.h>
|
|
||||||
#include <pulse/introspect.h>
|
|
||||||
|
|
||||||
|
|
||||||
void channel_map_to_lua(lua_State*, const pa_channel_map*);
|
|
||||||
void sample_spec_to_lua(lua_State*, const pa_sample_spec*);
|
|
||||||
void sink_port_info_to_lua(lua_State*, const pa_sink_port_info*);
|
|
||||||
void source_port_info_to_lua(lua_State*, const pa_source_port_info*);
|
|
||||||
void format_info_to_lua(lua_State*, const pa_format_info*);
|
|
||||||
void sink_ports_to_lua(lua_State*, pa_sink_port_info**, int, pa_sink_port_info*);
|
|
||||||
void source_ports_to_lua(lua_State*, pa_source_port_info**, int, pa_source_port_info*);
|
|
||||||
void formats_to_lua(lua_State*, pa_format_info**, int);
|
|
||||||
void sink_info_to_lua(lua_State*, const pa_sink_info*);
|
|
||||||
void source_info_to_lua(lua_State*, const pa_source_info*);
|
|
||||||
void server_info_to_lua(lua_State*, const pa_server_info*);
|
|
||||||
void sink_input_info_to_lua(lua_State*, const pa_sink_input_info*);
|
|
||||||
void source_output_info_to_lua(lua_State*, const pa_source_output_info*);
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,22 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <lauxlib.h>
|
|
||||||
#include <lua.h>
|
|
||||||
|
|
||||||
#define LUA_MOD_EXPORT extern
|
|
||||||
|
|
||||||
#if LUA_VERSION_NUM <= 501
|
|
||||||
#define lua_rawlen lua_objlen
|
|
||||||
|
|
||||||
#define luaL_newlib(L, l) (luaL_newlibtable(L, l), luaL_setfuncs(L, l, 0))
|
|
||||||
#define luaL_newlibtable(L, l) (lua_createtable(L, 0, sizeof(l) / sizeof(l[0])))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if LUA_VERSION_NUM > 501
|
|
||||||
#define lua_equal(L, i1, i2) lua_compare(L, i1, i2, LUA_OPEQ)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct luaU_enumfield {
|
|
||||||
const char* name;
|
|
||||||
const char* value;
|
|
||||||
} luaU_enumfield;
|
|
|
@ -1,214 +0,0 @@
|
||||||
#include "proplist.h"
|
|
||||||
|
|
||||||
#include <pulse/xmalloc.h>
|
|
||||||
|
|
||||||
// Creates a Lua userdatum from/for a PulseAudio proplist.
|
|
||||||
//
|
|
||||||
// The provided proplist will be `pa_proplist_copy()`ed to pass memory management over
|
|
||||||
// to the garbage collector.
|
|
||||||
//
|
|
||||||
// @return[type=PropList]
|
|
||||||
int proplist_to_lua(lua_State* L, pa_proplist* pa_plist) {
|
|
||||||
proplist* plist = lua_newuserdata(L, sizeof(proplist));
|
|
||||||
if (pa_plist == NULL) {
|
|
||||||
lua_pushfstring(L, "Failed to allocate proplist userdata");
|
|
||||||
return lua_error(L);
|
|
||||||
}
|
|
||||||
luaL_getmetatable(L, LUA_PA_PROPLIST);
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
|
|
||||||
plist->plist = pa_proplist_copy(pa_plist);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int proplist_new(lua_State* L) {
|
|
||||||
pa_proplist* pa_plist = pa_proplist_new();
|
|
||||||
if (pa_plist == NULL) {
|
|
||||||
lua_pushfstring(L, "Failed to allocate PulseAudio proplist");
|
|
||||||
return lua_error(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
return proplist_to_lua(L, pa_plist);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int proplist_from_string(lua_State* L) {
|
|
||||||
const char* str = luaL_checkstring(L, 1);
|
|
||||||
pa_proplist* pa_plist = pa_proplist_from_string(str);
|
|
||||||
|
|
||||||
return proplist_to_lua(L, pa_plist);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int proplist_key_valid(lua_State* L) {
|
|
||||||
const char* key = luaL_checkstring(L, 1);
|
|
||||||
int valid = pa_proplist_key_valid(key);
|
|
||||||
lua_pushboolean(L, valid);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int proplist_isempty(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
int empty = pa_proplist_isempty(plist->plist);
|
|
||||||
lua_pushboolean(L, empty);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int proplist_tostring_sep(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
const char* sep = luaL_checkstring(L, 2);
|
|
||||||
const char* str = pa_proplist_to_string_sep(plist->plist, sep);
|
|
||||||
lua_pushstring(L, str);
|
|
||||||
pa_xfree((void*) str);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int proplist_clear(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
pa_proplist_clear(plist->plist);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int proplist_contains(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
const char* key = luaL_checkstring(L, 2);
|
|
||||||
if (!pa_proplist_key_valid(key)) {
|
|
||||||
return luaL_error(L, "invalid key for proplist");
|
|
||||||
}
|
|
||||||
|
|
||||||
int contains = pa_proplist_contains(plist->plist, key);
|
|
||||||
if (contains < 0) {
|
|
||||||
return luaL_error(L, "failed to check if proplist contains the key");
|
|
||||||
} else {
|
|
||||||
lua_pushboolean(L, contains);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int proplist_copy(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
pa_proplist* other = pa_proplist_copy(plist->plist);
|
|
||||||
|
|
||||||
return proplist_to_lua(L, other);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Metatable functions
|
|
||||||
|
|
||||||
|
|
||||||
// Accesses the value at the given string key.
|
|
||||||
//
|
|
||||||
// This functions very similar to a regular hash table.
|
|
||||||
//
|
|
||||||
// Numeric indices/keys are not supported.
|
|
||||||
//
|
|
||||||
// Will return `nil` if there is no value or if it is not valid UTF-8.
|
|
||||||
// Arbitrary data from `pa_proplist_get()` is currently not supported.
|
|
||||||
//
|
|
||||||
// @param[type=string] key The index to access.
|
|
||||||
// @return[type=string|nil] The data at the given index.
|
|
||||||
int proplist__index(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
const char* key = luaL_checkstring(L, 2);
|
|
||||||
if (!pa_proplist_key_valid(key)) {
|
|
||||||
return luaL_error(L, "invalid key for proplist");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pa_proplist_contains(plist->plist, key)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* value = pa_proplist_gets(plist->plist, key);
|
|
||||||
if (value == NULL) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
} else {
|
|
||||||
lua_pushstring(L, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Sets or overwrites the value at the given key.
|
|
||||||
//
|
|
||||||
// Passing `nil` as value will unset the key.
|
|
||||||
//
|
|
||||||
// For both keys and values only strings are supported.
|
|
||||||
// Numeric indices or binary data values (as in `pa_proplist_set()`) are not available.
|
|
||||||
//
|
|
||||||
// @param[type=string] key The index to write to.
|
|
||||||
// @param[type=string|nil] value The value to write.
|
|
||||||
int proplist__newindex(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
const char* key = luaL_checkstring(L, 2);
|
|
||||||
|
|
||||||
if (lua_isnil(L, 3)) {
|
|
||||||
if (pa_proplist_unset(plist->plist, key) < 0) {
|
|
||||||
// TODO: Get last error. Need to get access to the current context for
|
|
||||||
// this.
|
|
||||||
return luaL_error(L, "failed to unset key %s", key);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const char* value = luaL_checkstring(L, 3);
|
|
||||||
if (pa_proplist_sets(plist->plist, key, value) < 0) {
|
|
||||||
return luaL_error(L, "failed to set value for key %s", key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Frees the internal proplist.
|
|
||||||
int proplist__gc(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
pa_proplist_free(plist->plist);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Gets the size of the proplist.
|
|
||||||
int proplist__len(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
unsigned len = pa_proplist_size(plist->plist);
|
|
||||||
lua_pushinteger(L, len);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Compares two proplists for equality.
|
|
||||||
//
|
|
||||||
// Proplists are equal if they contain the same keys with the same values.
|
|
||||||
//
|
|
||||||
// @param[type=PropList] other The proplist to compare against.
|
|
||||||
// @return[type=boolean]
|
|
||||||
int proplist__eq(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
proplist* other = luaL_checkudata(L, 2, LUA_PA_PROPLIST);
|
|
||||||
int equal = pa_proplist_equal(plist->plist, other->plist);
|
|
||||||
lua_pushboolean(L, equal);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Creates a string representation.
|
|
||||||
//
|
|
||||||
// This adds a newline character as separator and as final character.
|
|
||||||
//
|
|
||||||
// @return[type=string]
|
|
||||||
int proplist__tostring(lua_State* L) {
|
|
||||||
proplist* plist = luaL_checkudata(L, 1, LUA_PA_PROPLIST);
|
|
||||||
char* str = pa_proplist_to_string(plist->plist);
|
|
||||||
lua_pushstring(L, str);
|
|
||||||
pa_xfree((void*) str);
|
|
||||||
return 1;
|
|
||||||
}
|
|
|
@ -1,318 +0,0 @@
|
||||||
/** Bindings for PulseAudio's property lists.
|
|
||||||
*
|
|
||||||
* Basic operations are mapped to Lua table operations:
|
|
||||||
*
|
|
||||||
* - get a value: `plist[key]`
|
|
||||||
* - set a value: `plist[key] = value`
|
|
||||||
* - get the size: `#plist`
|
|
||||||
* - get default string presentation: `tostring(plist)`
|
|
||||||
* - equality: `plist == other`
|
|
||||||
*
|
|
||||||
* Additional operations are exposed as methods, as documented below.
|
|
||||||
*
|
|
||||||
* @module lua_libpulse_glib.proplist
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "lua_util.h"
|
|
||||||
|
|
||||||
#include <lauxlib.h>
|
|
||||||
#include <lua.h>
|
|
||||||
#include <pulse/proplist.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#define LUA_PA_PROPLIST "lua_libpulse_glib.proplist"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct proplist {
|
|
||||||
pa_proplist* plist;
|
|
||||||
} proplist;
|
|
||||||
|
|
||||||
|
|
||||||
/** A list of well-known keys. These will likely be used by other consumers
|
|
||||||
* of PulseAudio, so should be preferred for interoperatibility.
|
|
||||||
*
|
|
||||||
* See [proplist.h](https://freedesktop.org/software/pulseaudio/doxygen/proplist_8h.html) for details on
|
|
||||||
* these values.
|
|
||||||
*
|
|
||||||
* local proplist = require("pulseaudio.proplist")
|
|
||||||
* proplist.is_key_valid(proplist.MEDIA_NAME) -- true
|
|
||||||
* print(proplist.MEDIA_NAME) -- media.name
|
|
||||||
*
|
|
||||||
* @table pulseaudio.proplist
|
|
||||||
* @field MEDIA_NAME media.name
|
|
||||||
* @field MEDIA_TITLE media.title
|
|
||||||
* @field MEDIA_ARTIST media.artist
|
|
||||||
* @field MEDIA_COPYRIGHT media.copyright
|
|
||||||
* @field MEDIA_SOFTWARE media.software
|
|
||||||
* @field MEDIA_LANGUAGE media.language
|
|
||||||
* @field MEDIA_FILENAME media.filename
|
|
||||||
* @field MEDIA_ICON media.icon
|
|
||||||
* @field MEDIA_ICON_NAME media.icon_name
|
|
||||||
* @field MEDIA_ROLE media.role
|
|
||||||
* @field FILTER_WANT filter.want
|
|
||||||
* @field FILTER_APPLY filter.apply
|
|
||||||
* @field FILTER_SUPPRESS filter.suppress
|
|
||||||
* @field EVENT_ID event.id
|
|
||||||
* @field EVENT_DESCRIPTION event.description
|
|
||||||
* @field EVENT_MOUSE_X event.mouse.x
|
|
||||||
* @field EVENT_MOUSE_Y event.mouse.y
|
|
||||||
* @field EVENT_MOUSE_HPOS event.mouse.hpos
|
|
||||||
* @field EVENT_MOUSE_VPOS event.mouse.vpos
|
|
||||||
* @field EVENT_MOUSE_BUTTON event.mouse.button
|
|
||||||
* @field WINDOW_NAME window.name
|
|
||||||
* @field WINDOW_ID window.id
|
|
||||||
* @field WINDOW_ICON window.icon
|
|
||||||
* @field WINDOW_ICON_NAME window.icon_name
|
|
||||||
* @field WINDOW_X window.x
|
|
||||||
* @field WINDOW_Y window.y
|
|
||||||
* @field WINDOW_WIDTH window.width
|
|
||||||
* @field WINDOW_HEIGHT window.height
|
|
||||||
* @field WINDOW_HPOS window.hpos
|
|
||||||
* @field WINDOW_VPOS window.vpos
|
|
||||||
* @field WINDOW_DESKTOP window.desktop
|
|
||||||
* @field WINDOW_X11_DISPLAY window.x11.display
|
|
||||||
* @field WINDOW_X11_SCREEN window.x11.screen
|
|
||||||
* @field WINDOW_X11_MONITOR window.x11.monitor
|
|
||||||
* @field WINDOW_X11_XID window.x11.xid
|
|
||||||
* @field APPLICATION_NAME application.name
|
|
||||||
* @field APPLICATION_ID application.id
|
|
||||||
* @field APPLICATION_VERSION application.version
|
|
||||||
* @field APPLICATION_ICON application.icon
|
|
||||||
* @field APPLICATION_ICON_NAME application.icon_name
|
|
||||||
* @field APPLICATION_LANGUAGE application.language
|
|
||||||
* @field APPLICATION_PROCESS_ID application.process.id
|
|
||||||
* @field APPLICATION_PROCESS_BINARY application.process.binary
|
|
||||||
* @field APPLICATION_PROCESS_USER application.process.user
|
|
||||||
* @field APPLICATION_PROCESS_HOST application.process.host
|
|
||||||
* @field APPLICATION_PROCESS_MACHINE_ID application.process.machine_id
|
|
||||||
* @field APPLICATION_PROCESS_SESSION_ID application.process.session_id
|
|
||||||
* @field DEVICE_STRING device.string
|
|
||||||
* @field DEVICE_API device.api
|
|
||||||
* @field DEVICE_DESCRIPTION device.description
|
|
||||||
* @field DEVICE_BUS_PATH device.bus_path
|
|
||||||
* @field DEVICE_SERIAL device.serial
|
|
||||||
* @field DEVICE_VENDOR_ID device.vendor.id
|
|
||||||
* @field DEVICE_VENDOR_NAME device.vendor.name
|
|
||||||
* @field DEVICE_PRODUCT_ID device.product.id
|
|
||||||
* @field DEVICE_PRODUCT_NAME device.product.name
|
|
||||||
* @field DEVICE_CLASS device.class
|
|
||||||
* @field DEVICE_FORM_FACTOR device.form_factor
|
|
||||||
* @field DEVICE_BUS device.bus
|
|
||||||
* @field DEVICE_ICON device.icon
|
|
||||||
* @field DEVICE_ICON_NAME device.icon_name
|
|
||||||
* @field DEVICE_ACCESS_MODE device.access_mode
|
|
||||||
* @field DEVICE_MASTER_DEVICE device.master_device
|
|
||||||
* @field DEVICE_BUFFERING_BUFFER_SIZE device.buffering.buffer_size
|
|
||||||
* @field DEVICE_BUFFERING_FRAGMENT_SIZE device.buffering.fragment_size
|
|
||||||
* @field DEVICE_PROFILE_NAME device.profile.name
|
|
||||||
* @field DEVICE_INTENDED_ROLES device.intended_roles
|
|
||||||
* @field DEVICE_PROFILE_DESCRIPTION device.profile.description
|
|
||||||
* @field MODULE_AUTHOR module.author
|
|
||||||
* @field MODULE_DESCRIPTION module.description
|
|
||||||
* @field MODULE_USAGE module.usage
|
|
||||||
* @field MODULE_VERSION module.version
|
|
||||||
* @field FORMAT_SAMPLE_FORMAT format.sample_format
|
|
||||||
* @field FORMAT_RATE format.rate
|
|
||||||
* @field FORMAT_CHANNELS format.channels
|
|
||||||
* @field FORMAT_CHANNEL_MAP format.channel_map
|
|
||||||
* @field CONTEXT_FORCE_DISABLE_SHM context.force.disable.shm
|
|
||||||
* @field BLUETOOTH_CODEC bluetooth.codec
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/// Constructor functions.
|
|
||||||
/// @section constructors
|
|
||||||
|
|
||||||
|
|
||||||
/** Creates a new, empty property list.
|
|
||||||
*
|
|
||||||
* @function new
|
|
||||||
* @return[type=PropList]
|
|
||||||
*/
|
|
||||||
int proplist_new(lua_State*);
|
|
||||||
|
|
||||||
/** Parses a string into a @{PropList}.
|
|
||||||
*
|
|
||||||
* @function from_string
|
|
||||||
* @param[type=string] str The string to parse.
|
|
||||||
* @return[type=PropList]
|
|
||||||
*/
|
|
||||||
int proplist_from_string(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
/// Static functions
|
|
||||||
/// @section static
|
|
||||||
|
|
||||||
|
|
||||||
/** Checks if the given string is a valid key.
|
|
||||||
*
|
|
||||||
* @function is_key_valid
|
|
||||||
* @param[type=string] key The string to check.
|
|
||||||
* @return[type=boolean]
|
|
||||||
*/
|
|
||||||
int proplist_key_valid(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
// Internal functions
|
|
||||||
|
|
||||||
int proplist_to_lua(lua_State*, pa_proplist*);
|
|
||||||
int proplist__index(lua_State*);
|
|
||||||
int proplist__newindex(lua_State*);
|
|
||||||
int proplist__gc(lua_State*);
|
|
||||||
int proplist__len(lua_State*);
|
|
||||||
int proplist__eq(lua_State*);
|
|
||||||
int proplist__tostring(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
/// Methods
|
|
||||||
/// @type PropList
|
|
||||||
|
|
||||||
|
|
||||||
/** Checks if the proplist is empty.
|
|
||||||
*
|
|
||||||
* @function is_empty
|
|
||||||
* @return[type=boolean]
|
|
||||||
*/
|
|
||||||
int proplist_isempty(lua_State*);
|
|
||||||
|
|
||||||
/** Creates a string representation with a custom separator.
|
|
||||||
*
|
|
||||||
* @function tostring_sep
|
|
||||||
* @return[type=string]
|
|
||||||
*/
|
|
||||||
int proplist_tostring_sep(lua_State*);
|
|
||||||
|
|
||||||
/** Removes all keys from the proplist
|
|
||||||
*
|
|
||||||
* @function clear
|
|
||||||
*/
|
|
||||||
int proplist_clear(lua_State*);
|
|
||||||
|
|
||||||
/** Checks if the proplist contains the given key.
|
|
||||||
*
|
|
||||||
* @function contains
|
|
||||||
* @param[type=string] key The key to check for.
|
|
||||||
* @return[type=boolean]
|
|
||||||
*/
|
|
||||||
int proplist_contains(lua_State*);
|
|
||||||
|
|
||||||
/** Duplicates the proplist.
|
|
||||||
*
|
|
||||||
* @function copy
|
|
||||||
* @return[type=proplist]
|
|
||||||
*/
|
|
||||||
int proplist_copy(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg proplist_f[] = {
|
|
||||||
{"clear", proplist_clear },
|
|
||||||
{ "contains", proplist_contains },
|
|
||||||
{ "copy", proplist_copy },
|
|
||||||
{ "is_empty", proplist_isempty },
|
|
||||||
{ "tostring_sep", proplist_tostring_sep},
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg proplist_mt[] = {
|
|
||||||
{"__gc", proplist__gc },
|
|
||||||
{ "__len", proplist__len },
|
|
||||||
{ "__tostring", proplist__tostring},
|
|
||||||
{ "__eq", proplist__eq },
|
|
||||||
{ "__index", proplist__index },
|
|
||||||
{ "__newindex", proplist__newindex},
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg proplist_lib[] = {
|
|
||||||
{"new", proplist_new },
|
|
||||||
{ "is_key_valid", proplist_key_valid },
|
|
||||||
{ "from_string", proplist_from_string},
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaU_enumfield proplist_enum[] = {
|
|
||||||
{"MEDIA_TITLE", PA_PROP_MEDIA_TITLE },
|
|
||||||
{ "MEDIA_ARTIST", PA_PROP_MEDIA_ARTIST },
|
|
||||||
{ "MEDIA_COPYRIGHT", PA_PROP_MEDIA_COPYRIGHT },
|
|
||||||
{ "MEDIA_SOFTWARE", PA_PROP_MEDIA_SOFTWARE },
|
|
||||||
{ "MEDIA_LANGUAGE", PA_PROP_MEDIA_LANGUAGE },
|
|
||||||
{ "MEDIA_FILENAME", PA_PROP_MEDIA_FILENAME },
|
|
||||||
{ "MEDIA_ICON", PA_PROP_MEDIA_ICON },
|
|
||||||
{ "MEDIA_ICON_NAME", PA_PROP_MEDIA_ICON_NAME },
|
|
||||||
{ "MEDIA_ROLE", PA_PROP_MEDIA_ROLE },
|
|
||||||
{ "FILTER_WANT", PA_PROP_FILTER_WANT },
|
|
||||||
{ "FILTER_APPLY", PA_PROP_FILTER_APPLY },
|
|
||||||
{ "FILTER_SUPPRESS", PA_PROP_FILTER_SUPPRESS },
|
|
||||||
{ "EVENT_ID", PA_PROP_EVENT_ID },
|
|
||||||
{ "EVENT_DESCRIPTION", PA_PROP_EVENT_DESCRIPTION },
|
|
||||||
{ "EVENT_MOUSE_X", PA_PROP_EVENT_MOUSE_X },
|
|
||||||
{ "EVENT_MOUSE_Y", PA_PROP_EVENT_MOUSE_Y },
|
|
||||||
{ "EVENT_MOUSE_HPOS", PA_PROP_EVENT_MOUSE_HPOS },
|
|
||||||
{ "EVENT_MOUSE_VPOS", PA_PROP_EVENT_MOUSE_VPOS },
|
|
||||||
{ "EVENT_MOUSE_BUTTON", PA_PROP_EVENT_MOUSE_BUTTON },
|
|
||||||
{ "WINDOW_NAME", PA_PROP_WINDOW_NAME },
|
|
||||||
{ "WINDOW_ID", PA_PROP_WINDOW_ID },
|
|
||||||
{ "WINDOW_ICON", PA_PROP_WINDOW_ICON },
|
|
||||||
{ "WINDOW_ICON_NAME", PA_PROP_WINDOW_ICON_NAME },
|
|
||||||
{ "WINDOW_X", PA_PROP_WINDOW_X },
|
|
||||||
{ "WINDOW_Y", PA_PROP_WINDOW_Y },
|
|
||||||
{ "WINDOW_WIDTH", PA_PROP_WINDOW_WIDTH },
|
|
||||||
{ "WINDOW_HEIGHT", PA_PROP_WINDOW_HEIGHT },
|
|
||||||
{ "WINDOW_HPOS", PA_PROP_WINDOW_HPOS },
|
|
||||||
{ "WINDOW_VPOS", PA_PROP_WINDOW_VPOS },
|
|
||||||
{ "WINDOW_DESKTOP", PA_PROP_WINDOW_DESKTOP },
|
|
||||||
{ "WINDOW_X11_DISPLAY", PA_PROP_WINDOW_X11_DISPLAY },
|
|
||||||
{ "WINDOW_X11_SCREEN", PA_PROP_WINDOW_X11_SCREEN },
|
|
||||||
{ "WINDOW_X11_MONITOR", PA_PROP_WINDOW_X11_MONITOR },
|
|
||||||
{ "WINDOW_X11_XID", PA_PROP_WINDOW_X11_XID },
|
|
||||||
{ "APPLICATION_NAME", PA_PROP_APPLICATION_NAME },
|
|
||||||
{ "APPLICATION_ID", PA_PROP_APPLICATION_ID },
|
|
||||||
{ "APPLICATION_VERSION", PA_PROP_APPLICATION_VERSION },
|
|
||||||
{ "APPLICATION_ICON", PA_PROP_APPLICATION_ICON },
|
|
||||||
{ "APPLICATION_ICON_NAME", PA_PROP_APPLICATION_ICON_NAME },
|
|
||||||
{ "APPLICATION_LANGUAGE", PA_PROP_APPLICATION_LANGUAGE },
|
|
||||||
{ "APPLICATION_PROCESS_ID", PA_PROP_APPLICATION_PROCESS_ID },
|
|
||||||
{ "APPLICATION_PROCESS_BINARY", PA_PROP_APPLICATION_PROCESS_BINARY },
|
|
||||||
{ "APPLICATION_PROCESS_USER", PA_PROP_APPLICATION_PROCESS_USER },
|
|
||||||
{ "APPLICATION_PROCESS_HOST", PA_PROP_APPLICATION_PROCESS_HOST },
|
|
||||||
{ "APPLICATION_PROCESS_MACHINE_ID", PA_PROP_APPLICATION_PROCESS_MACHINE_ID},
|
|
||||||
{ "APPLICATION_PROCESS_SESSION_ID", PA_PROP_APPLICATION_PROCESS_SESSION_ID},
|
|
||||||
{ "DEVICE_STRING", PA_PROP_DEVICE_STRING },
|
|
||||||
{ "DEVICE_API", PA_PROP_DEVICE_API },
|
|
||||||
{ "DEVICE_DESCRIPTION", PA_PROP_DEVICE_DESCRIPTION },
|
|
||||||
{ "DEVICE_BUS_PATH", PA_PROP_DEVICE_BUS_PATH },
|
|
||||||
{ "DEVICE_SERIAL", PA_PROP_DEVICE_SERIAL },
|
|
||||||
{ "DEVICE_VENDOR_ID", PA_PROP_DEVICE_VENDOR_ID },
|
|
||||||
{ "DEVICE_VENDOR_NAME", PA_PROP_DEVICE_VENDOR_NAME },
|
|
||||||
{ "DEVICE_PRODUCT_ID", PA_PROP_DEVICE_PRODUCT_ID },
|
|
||||||
{ "DEVICE_PRODUCT_NAME", PA_PROP_DEVICE_PRODUCT_NAME },
|
|
||||||
{ "DEVICE_CLASS", PA_PROP_DEVICE_CLASS },
|
|
||||||
{ "DEVICE_FORM_FACTOR", PA_PROP_DEVICE_FORM_FACTOR },
|
|
||||||
{ "DEVICE_BUS", PA_PROP_DEVICE_BUS },
|
|
||||||
{ "DEVICE_ICON", PA_PROP_DEVICE_ICON },
|
|
||||||
{ "DEVICE_ICON_NAME", PA_PROP_DEVICE_ICON_NAME },
|
|
||||||
{ "DEVICE_ACCESS_MODE", PA_PROP_DEVICE_ACCESS_MODE },
|
|
||||||
{ "DEVICE_MASTER_DEVICE", PA_PROP_DEVICE_MASTER_DEVICE },
|
|
||||||
{ "DEVICE_BUFFERING_BUFFER_SIZE", PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE },
|
|
||||||
{ "DEVICE_BUFFERING_FRAGMENT_SIZE", PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE},
|
|
||||||
{ "DEVICE_PROFILE_NAME", PA_PROP_DEVICE_PROFILE_NAME },
|
|
||||||
{ "DEVICE_INTENDED_ROLES", PA_PROP_DEVICE_INTENDED_ROLES },
|
|
||||||
{ "DEVICE_PROFILE_DESCRIPTION", PA_PROP_DEVICE_PROFILE_DESCRIPTION },
|
|
||||||
{ "MODULE_AUTHOR", PA_PROP_MODULE_AUTHOR },
|
|
||||||
{ "MODULE_DESCRIPTION", PA_PROP_MODULE_DESCRIPTION },
|
|
||||||
{ "MODULE_USAGE", PA_PROP_MODULE_USAGE },
|
|
||||||
{ "MODULE_VERSION", PA_PROP_MODULE_VERSION },
|
|
||||||
{ "FORMAT_SAMPLE_FORMAT", PA_PROP_FORMAT_SAMPLE_FORMAT },
|
|
||||||
{ "FORMAT_RATE", PA_PROP_FORMAT_RATE },
|
|
||||||
{ "FORMAT_CHANNELS", PA_PROP_FORMAT_CHANNELS },
|
|
||||||
{ "FORMAT_CHANNEL_MAP", PA_PROP_FORMAT_CHANNEL_MAP },
|
|
||||||
#if PA_CHECK_VERSION(15, 0, 0)
|
|
||||||
{ "CONTEXT_FORCE_DISABLE_SHM", PA_PROP_CONTEXT_FORCE_DISABLE_SHM },
|
|
||||||
{ "BLUETOOTH_CODEC", PA_PROP_BLUETOOTH_CODEC },
|
|
||||||
#endif
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
|
@ -1,166 +0,0 @@
|
||||||
#include "pulseaudio.h"
|
|
||||||
|
|
||||||
#include "context.h"
|
|
||||||
#include "lua_util.h"
|
|
||||||
#include "proplist.h"
|
|
||||||
#include "volume.h"
|
|
||||||
|
|
||||||
#include <lauxlib.h>
|
|
||||||
#include <lua.h>
|
|
||||||
#include <lualib.h>
|
|
||||||
#include <pulse/glib-mainloop.h>
|
|
||||||
|
|
||||||
#if LUA_VERSION_NUM <= 501
|
|
||||||
// Shamelessly copied from Lua 5.3 source.
|
|
||||||
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 */
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int pulseaudio_new(lua_State* L) {
|
|
||||||
GMainContext* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pulseaudio* pa = lua_newuserdata(L, sizeof(pulseaudio));
|
|
||||||
if (!pa) {
|
|
||||||
return luaL_error(L, "failed to create pulseaudio userdata");
|
|
||||||
}
|
|
||||||
luaL_getmetatable(L, LUA_PULSEAUDIO);
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
|
|
||||||
pa->mainloop = pa_glib_mainloop_new(ctx);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Proxies table index operations to our metatable.
|
|
||||||
*/
|
|
||||||
int pulseaudio__index(lua_State* L) {
|
|
||||||
const char* index = luaL_checkstring(L, 2);
|
|
||||||
luaL_getmetatable(L, LUA_PULSEAUDIO);
|
|
||||||
lua_getfield(L, -1, index);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Free the PulseAudio object
|
|
||||||
*/
|
|
||||||
int pulseaudio__gc(lua_State* L) {
|
|
||||||
pulseaudio* pa = luaL_checkudata(L, 1, LUA_PULSEAUDIO);
|
|
||||||
pa_glib_mainloop_free(pa->mainloop);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int pulseaudio_new_context(lua_State* L) {
|
|
||||||
pulseaudio* pa = luaL_checkudata(L, 1, LUA_PULSEAUDIO);
|
|
||||||
return context_new(L, pa_glib_mainloop_get_api(pa->mainloop));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void createlib_volume(lua_State* L) {
|
|
||||||
luaL_newmetatable(L, LUA_PA_VOLUME);
|
|
||||||
|
|
||||||
luaL_newlib(L, volume_f);
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
|
|
||||||
luaL_setfuncs(L, volume_mt, 0);
|
|
||||||
|
|
||||||
luaL_newlib(L, volume_lib);
|
|
||||||
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void createlib_proplist(lua_State* L) {
|
|
||||||
luaL_newmetatable(L, LUA_PA_PROPLIST);
|
|
||||||
|
|
||||||
luaL_newlib(L, proplist_f);
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
|
|
||||||
luaL_setfuncs(L, proplist_mt, 0);
|
|
||||||
|
|
||||||
luaL_newlib(L, proplist_lib);
|
|
||||||
|
|
||||||
// Create a metatable with an `__index` table for read-only enum fields.
|
|
||||||
lua_createtable(L, 0, 1);
|
|
||||||
|
|
||||||
lua_createtable(L, 0, sizeof proplist_enum / sizeof proplist_enum[0]);
|
|
||||||
for (int i = 0; proplist_enum[i].name != NULL; ++i) {
|
|
||||||
lua_pushstring(L, proplist_enum[i].value);
|
|
||||||
lua_setfield(L, -2, proplist_enum[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void createlib_context(lua_State* L) {
|
|
||||||
luaL_newmetatable(L, LUA_PA_CONTEXT);
|
|
||||||
|
|
||||||
luaL_newlib(L, context_f);
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
|
|
||||||
luaL_setfuncs(L, context_mt, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void createlib_pulseaudio(lua_State* L) {
|
|
||||||
luaL_newmetatable(L, LUA_PULSEAUDIO);
|
|
||||||
|
|
||||||
luaL_newlib(L, pulseaudio_f);
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
|
|
||||||
luaL_setfuncs(L, pulseaudio_mt, 0);
|
|
||||||
|
|
||||||
luaL_newlib(L, pulseaudio_lib);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LUA_MOD_EXPORT int luaopen_lua_libpulse_glib(lua_State* L) {
|
|
||||||
// Create a table to store callback refs in, stored in the Lua registry
|
|
||||||
lua_pushstring(L, LUA_PULSEAUDIO);
|
|
||||||
lua_newtable(L);
|
|
||||||
lua_pushstring(L, LUA_PA_REGISTRY);
|
|
||||||
lua_newtable(L);
|
|
||||||
lua_settable(L, -3);
|
|
||||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
||||||
|
|
||||||
createlib_context(L);
|
|
||||||
createlib_proplist(L);
|
|
||||||
createlib_pulseaudio(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LUA_MOD_EXPORT int luaopen_lua_libpulse_glib_volume(lua_State* L) {
|
|
||||||
luaL_newmetatable(L, LUA_PA_VOLUME);
|
|
||||||
|
|
||||||
luaL_newlib(L, volume_f);
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
|
|
||||||
luaL_setfuncs(L, volume_mt, 0);
|
|
||||||
|
|
||||||
luaL_newlib(L, volume_lib);
|
|
||||||
return 1;
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/** Bindings for PulseAudio's libpulse, using the GLib Main Loop.
|
|
||||||
*
|
|
||||||
* @module lua_libpulse_glib
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "lauxlib.h"
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
#include <pulse/glib-mainloop.h>
|
|
||||||
|
|
||||||
#define LUA_PULSEAUDIO "lua_libpulse_glib"
|
|
||||||
#define LUA_PA_REGISTRY "lua_libpulse_glib.registry"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct pulseaudio {
|
|
||||||
pa_glib_mainloop* mainloop;
|
|
||||||
} pulseaudio;
|
|
||||||
|
|
||||||
|
|
||||||
/** Creates a new PulseAudio object.
|
|
||||||
*
|
|
||||||
* @function new
|
|
||||||
* @return[type=PulseAudio]
|
|
||||||
*/
|
|
||||||
int pulseaudio_new(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
int pulseaudio__gc(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
/// PulseAudio API
|
|
||||||
/// @type PulseAudio
|
|
||||||
|
|
||||||
|
|
||||||
/** Creates a new PulseAudio context
|
|
||||||
*
|
|
||||||
* @function context
|
|
||||||
* @tparam string name The application name.
|
|
||||||
* @return[type=Context]
|
|
||||||
*/
|
|
||||||
int pulseaudio_new_context(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg pulseaudio_mt[] = {
|
|
||||||
{"__gc", pulseaudio__gc},
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg pulseaudio_f[] = {
|
|
||||||
{"context", pulseaudio_new_context},
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg pulseaudio_lib[] = {
|
|
||||||
{"new", pulseaudio_new},
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
|
@ -1,343 +0,0 @@
|
||||||
#include "volume.h"
|
|
||||||
|
|
||||||
#include "lua_util.h"
|
|
||||||
|
|
||||||
#include <pulse/xmalloc.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
int volume_new(lua_State* L) {
|
|
||||||
pa_cvolume* cvol = volume_from_lua(L, 1);
|
|
||||||
return volume_to_lua(L, cvol);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_to_lua(lua_State* L, const pa_cvolume* pa_volume) {
|
|
||||||
volume_t* volume = lua_newuserdata(L, sizeof(volume_t));
|
|
||||||
if (pa_volume == NULL) {
|
|
||||||
lua_pushfstring(L, "Failed to allocate volume userdata");
|
|
||||||
return lua_error(L);
|
|
||||||
}
|
|
||||||
luaL_getmetatable(L, LUA_PA_VOLUME);
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
|
|
||||||
volume->inner = *pa_volume;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// When the value is a plain Lua table, a new `pa_cvolume` is allocated and the caller is
|
|
||||||
// responsible of freeing it with `pa_xfree`.
|
|
||||||
// If a userdata is passed instead, memory is owned by Lua and not be freed.
|
|
||||||
// TODO: Maybe construct userdata from the table, so that memory management is always handled
|
|
||||||
// by GC. Makes this easier to use. But only do so after the current version has been committed
|
|
||||||
// once, just so it stays available.
|
|
||||||
pa_cvolume* volume_from_lua(lua_State* L, int index) {
|
|
||||||
switch (lua_type(L, index)) {
|
|
||||||
case LUA_TTABLE: {
|
|
||||||
uint8_t channels = (uint8_t) lua_rawlen(L, index);
|
|
||||||
if (channels > PA_CHANNELS_MAX) {
|
|
||||||
channels = PA_CHANNELS_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
pa_cvolume* volume = pa_xnew(pa_cvolume, 1);
|
|
||||||
volume->channels = channels;
|
|
||||||
|
|
||||||
for (int i = 0; i < channels; ++i) {
|
|
||||||
lua_pushinteger(L, i + 1);
|
|
||||||
lua_gettable(L, index);
|
|
||||||
pa_volume_t vol = (pa_volume_t) luaL_checkinteger(L, -1);
|
|
||||||
volume->values[i] = PA_CLAMP_VOLUME(vol);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return volume;
|
|
||||||
}
|
|
||||||
case LUA_TUSERDATA: {
|
|
||||||
volume_t* volume = luaL_checkudata(L, index, LUA_PA_VOLUME);
|
|
||||||
return &volume->inner;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
luaL_argerror(L, index, "expected table or userdata");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume__len(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_pushinteger(L, volume->inner.channels);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume__eq(lua_State* L) {
|
|
||||||
volume_t* left = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
volume_t* right = luaL_checkudata(L, 2, LUA_PA_VOLUME);
|
|
||||||
lua_pushboolean(L, pa_cvolume_equal(&left->inner, &right->inner));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume__index(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
int index = (int) luaL_checkinteger(L, 2);
|
|
||||||
luaL_argcheck(L, index >= 1 && index <= (PA_CHANNELS_MAX + 1), 2, "channel index out of bounds");
|
|
||||||
lua_pushinteger(L, volume->inner.values[index - 1]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume__newindex(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_Integer index = luaL_checkinteger(L, 2);
|
|
||||||
lua_Integer value = luaL_checkinteger(L, 3);
|
|
||||||
luaL_argcheck(L, index >= 1 && index <= (PA_CHANNELS_MAX + 1), 2, "channel index out of bounds");
|
|
||||||
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 3, "volume value is invalid");
|
|
||||||
volume->inner.values[index - 1] = value;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume__tostring(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
char s[PA_CVOLUME_SNPRINT_MAX];
|
|
||||||
pa_cvolume_snprint(s, PA_CVOLUME_SNPRINT_MAX, &volume->inner);
|
|
||||||
lua_pushstring(L, s);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int volume_is_valid(lua_State* L) {
|
|
||||||
// Lua's `checkudata` throws an error, and catching that is expensive.
|
|
||||||
// So we have to manually implement the check.
|
|
||||||
volume_t* volume = lua_touserdata(L, 1);
|
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, LUA_PA_VOLUME);
|
|
||||||
bool is_userdata = volume != NULL && lua_getmetatable(L, 1) && lua_rawequal(L, -1, -2);
|
|
||||||
// Remove the two metatables
|
|
||||||
lua_pop(L, 2);
|
|
||||||
lua_pushboolean(L, is_userdata && pa_cvolume_valid(&volume->inner));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_channels_equal_to(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_Integer value = luaL_checkinteger(L, 2);
|
|
||||||
|
|
||||||
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
|
||||||
|
|
||||||
lua_pushboolean(L, pa_cvolume_channels_equal_to(&volume->inner, (pa_volume_t) value));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_is_muted(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_pushboolean(L, pa_cvolume_is_muted(&volume->inner));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_is_norm(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_pushboolean(L, pa_cvolume_is_norm(&volume->inner));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_set_channels(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_Integer channels = luaL_checkinteger(L, 2);
|
|
||||||
lua_Integer value = luaL_checkinteger(L, 3);
|
|
||||||
|
|
||||||
luaL_argcheck(L, channels >= 0 && channels <= PA_CHANNELS_MAX, 2, "channel count out of bounds");
|
|
||||||
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 3, "volume out of bounds");
|
|
||||||
|
|
||||||
pa_cvolume_set(&volume->inner, (unsigned int) channels, (pa_volume_t) value);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_set(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_Integer index = luaL_checkinteger(L, 2) - 1;
|
|
||||||
lua_Integer value = luaL_checkinteger(L, 3);
|
|
||||||
|
|
||||||
luaL_argcheck(L, index >= 0 && index <= PA_CHANNELS_MAX, 2, "channel index out of bounds");
|
|
||||||
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 3, "volume out of bounds");
|
|
||||||
|
|
||||||
volume->inner.values[index] = (pa_volume_t) value;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_get(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_Integer index = luaL_checkinteger(L, 2) - 1;
|
|
||||||
|
|
||||||
luaL_argcheck(L, index > 0 && index < volume->inner.channels, 2, "channel index out of bounds");
|
|
||||||
|
|
||||||
lua_pushinteger(L, volume->inner.values[index]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_reset(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_Integer channels = luaL_checkinteger(L, 2);
|
|
||||||
luaL_argcheck(L, channels >= 0 && channels <= PA_CHANNELS_MAX, 2, "channel count out of bounds");
|
|
||||||
pa_cvolume_reset(&volume->inner, channels);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_mute(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_Integer channels = luaL_checkinteger(L, 2);
|
|
||||||
luaL_argcheck(L, channels >= 0 && channels <= PA_CHANNELS_MAX, 2, "channel count out of bounds");
|
|
||||||
pa_cvolume_mute(&volume->inner, channels);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_avg(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_pushinteger(L, pa_cvolume_avg(&volume->inner));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_min(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_pushinteger(L, pa_cvolume_min(&volume->inner));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_max(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_pushinteger(L, pa_cvolume_max(&volume->inner));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_inc(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_Integer value = luaL_checkinteger(L, 2);
|
|
||||||
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
|
||||||
if (!pa_cvolume_inc(&volume->inner, (pa_volume_t) value)) {
|
|
||||||
return luaL_error(L, "failed to increase volume");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_dec(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_Integer value = luaL_checkinteger(L, 2);
|
|
||||||
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
|
||||||
if (!pa_cvolume_dec(&volume->inner, (pa_volume_t) value)) {
|
|
||||||
return luaL_error(L, "failed to decrease volume");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_scale(lua_State* L) {
|
|
||||||
volume_t* volume = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
lua_Integer value = luaL_checkinteger(L, 2);
|
|
||||||
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
|
||||||
if (!pa_cvolume_scale(&volume->inner, (pa_volume_t) value)) {
|
|
||||||
return luaL_error(L, "failed to scale volume");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_multiply(lua_State* L) {
|
|
||||||
volume_t* left = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
|
|
||||||
switch (lua_type(L, 2)) {
|
|
||||||
case LUA_TNUMBER: {
|
|
||||||
lua_Integer value = luaL_checkinteger(L, 2);
|
|
||||||
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
|
||||||
if (!pa_sw_cvolume_multiply_scalar(&left->inner, &left->inner, (pa_volume_t) value)) {
|
|
||||||
return luaL_error(L, "failed to multiply volume");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case LUA_TUSERDATA: {
|
|
||||||
volume_t* right = luaL_checkudata(L, 2, LUA_PA_VOLUME);
|
|
||||||
if (!pa_sw_cvolume_multiply(&left->inner, &left->inner, &right->inner)) {
|
|
||||||
return luaL_error(L, "failed to multiply volume");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return luaL_argerror(L, 2, "expected number or userdata");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int volume_divide(lua_State* L) {
|
|
||||||
volume_t* left = luaL_checkudata(L, 1, LUA_PA_VOLUME);
|
|
||||||
|
|
||||||
switch (lua_type(L, 2)) {
|
|
||||||
case LUA_TNUMBER: {
|
|
||||||
lua_Integer value = luaL_checkinteger(L, 2);
|
|
||||||
luaL_argcheck(L, PA_VOLUME_IS_VALID(value), 2, "volume out of bounds");
|
|
||||||
if (!pa_sw_cvolume_divide_scalar(&left->inner, &left->inner, (pa_volume_t) value)) {
|
|
||||||
return luaL_error(L, "failed to divide volume");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case LUA_TUSERDATA: {
|
|
||||||
volume_t* right = luaL_checkudata(L, 2, LUA_PA_VOLUME);
|
|
||||||
if (!pa_sw_cvolume_divide(&left->inner, &left->inner, &right->inner)) {
|
|
||||||
return luaL_error(L, "failed to divide volume");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return luaL_argerror(L, 2, "expected number or userdata");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_from_dB(lua_State* L) {
|
|
||||||
double value = (double) luaL_checknumber(L, 1);
|
|
||||||
lua_pushinteger(L, pa_sw_volume_from_dB(value));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_to_dB(lua_State* L) {
|
|
||||||
pa_volume_t value = luaL_checkinteger(L, 1);
|
|
||||||
lua_pushnumber(L, pa_sw_volume_to_dB(value));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_from_linear(lua_State* L) {
|
|
||||||
double value = (double) luaL_checknumber(L, 1);
|
|
||||||
lua_pushinteger(L, pa_sw_volume_from_linear(value));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int volume_to_linear(lua_State* L) {
|
|
||||||
pa_volume_t value = luaL_checkinteger(L, 1);
|
|
||||||
lua_pushnumber(L, pa_sw_volume_to_linear(value));
|
|
||||||
return 1;
|
|
||||||
}
|
|
|
@ -1,296 +0,0 @@
|
||||||
/** Bindings for libpulse's `pa_cvolume`.
|
|
||||||
*
|
|
||||||
* Contrary to libpulse, methods that change the a @{Volume} object generally don't return anything, but
|
|
||||||
* instead change the instance itself.
|
|
||||||
*
|
|
||||||
* @module lua_libpulse_glib.volume
|
|
||||||
*/
|
|
||||||
#ifndef volume_h_INCLUDED
|
|
||||||
#define volume_h_INCLUDED
|
|
||||||
|
|
||||||
#include <lauxlib.h>
|
|
||||||
#include <lua.h>
|
|
||||||
#include <pulse/volume.h>
|
|
||||||
|
|
||||||
#define LUA_PA_VOLUME "lua_libpulse_glib.volume"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct volume_t {
|
|
||||||
pa_cvolume inner;
|
|
||||||
} volume_t;
|
|
||||||
|
|
||||||
|
|
||||||
/* Creates a Lua userdatum from/for a PulseAudio volume.
|
|
||||||
*
|
|
||||||
* Control over the `pa_plist`'s memory has to be taken over by this function,
|
|
||||||
* to enable integration into Lua's garbage collection.
|
|
||||||
*
|
|
||||||
* @function volume_to_lua
|
|
||||||
* @treturn Volume
|
|
||||||
*/
|
|
||||||
int volume_to_lua(lua_State*, const pa_cvolume*);
|
|
||||||
|
|
||||||
/* Creates a pa_cvolume from Lua userdata
|
|
||||||
*/
|
|
||||||
pa_cvolume* volume_from_lua(lua_State*, int);
|
|
||||||
|
|
||||||
/* Implements the `#` length operator.
|
|
||||||
* This simply proxies to the `.channels` value.
|
|
||||||
*/
|
|
||||||
int volume__len(lua_State*);
|
|
||||||
int volume__eq(lua_State*);
|
|
||||||
int volume__tostring(lua_State*);
|
|
||||||
int volume__index(lua_State*);
|
|
||||||
int volume__newindex(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
/// Static Functions
|
|
||||||
/// @section static
|
|
||||||
|
|
||||||
/** Creates an instance of @{Volume}.
|
|
||||||
*
|
|
||||||
* @function new
|
|
||||||
* @tparam table values An array of channel volumes.
|
|
||||||
* @treturn Volume
|
|
||||||
*/
|
|
||||||
int volume_new(lua_State*);
|
|
||||||
|
|
||||||
/** Checks whether a value is a valid @{Volume}.
|
|
||||||
*
|
|
||||||
* @function is_valid
|
|
||||||
* @tparam any value The value to check
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int volume_is_valid(lua_State*);
|
|
||||||
|
|
||||||
/** Converts a decibel value to an integer volume value.
|
|
||||||
*
|
|
||||||
* This is only valid for software volumes. It does not operate
|
|
||||||
* on instances of @{Volume}.
|
|
||||||
*
|
|
||||||
* @function from_dB
|
|
||||||
* @tparam number value
|
|
||||||
* @treturn number
|
|
||||||
*/
|
|
||||||
int volume_from_dB(lua_State*);
|
|
||||||
|
|
||||||
/** Converts an integer volume value to a decibel value.
|
|
||||||
*
|
|
||||||
* This is only valid for software volumes. It does not operate
|
|
||||||
* on instances of @{Volume}.
|
|
||||||
*
|
|
||||||
* @function to_dB
|
|
||||||
* @tparam number value
|
|
||||||
* @treturn number
|
|
||||||
*/
|
|
||||||
int volume_to_dB(lua_State*);
|
|
||||||
|
|
||||||
/** Converts a linear factor to an integer volume value.
|
|
||||||
*
|
|
||||||
* `0.0` and less is muted, `1.0` is normal volume.
|
|
||||||
*
|
|
||||||
* This is only valid for software volumes. It does not operate
|
|
||||||
* on instances of @{Volume}.
|
|
||||||
*
|
|
||||||
* @function from_linear
|
|
||||||
* @tparam number value
|
|
||||||
* @treturn number
|
|
||||||
*/
|
|
||||||
int volume_from_linear(lua_State*);
|
|
||||||
|
|
||||||
/** Converts an integer volume value to linear factor.
|
|
||||||
*
|
|
||||||
* This is only valid for software volumes. It does not operate
|
|
||||||
* on instances of @{Volume}.
|
|
||||||
*
|
|
||||||
* @function to_linear
|
|
||||||
* @tparam number value
|
|
||||||
* @treturn number
|
|
||||||
*/
|
|
||||||
int volume_to_linear(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
/// Volume
|
|
||||||
/// @type Volume
|
|
||||||
|
|
||||||
/** Returns the average volume over all channels.
|
|
||||||
*
|
|
||||||
* @function Volume:avg
|
|
||||||
* @treturn number
|
|
||||||
*/
|
|
||||||
int volume_avg(lua_State*);
|
|
||||||
|
|
||||||
/** Returns `true` if the volume of all channels is equal to the specified value.
|
|
||||||
*
|
|
||||||
* @function Volume:channels_equal_to
|
|
||||||
* @tparam number value Volume to compare to
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int volume_channels_equal_to(lua_State*);
|
|
||||||
|
|
||||||
/** Decreases the volume by the given amount.
|
|
||||||
*
|
|
||||||
* The proportions between the channels are kept.
|
|
||||||
*
|
|
||||||
* @function Volume:dec
|
|
||||||
* @tparam number value The value to decrease by.
|
|
||||||
*/
|
|
||||||
int volume_dec(lua_State*);
|
|
||||||
|
|
||||||
/** Divides the volume by the given value.
|
|
||||||
*
|
|
||||||
* The value to divide by may either be a scalar, that's applied to all channels,
|
|
||||||
* or another instance of @{Volume}, which would be applied channel by channel.
|
|
||||||
*
|
|
||||||
* It is possible to divide a @{Volume} by itself.
|
|
||||||
*
|
|
||||||
* This is only valid for software volumes.
|
|
||||||
*
|
|
||||||
* @function Volume:divide
|
|
||||||
* @tparam number|Volume value The volume to divide by.
|
|
||||||
*/
|
|
||||||
int volume_divide(lua_State*);
|
|
||||||
|
|
||||||
/** Returns the volume of a single channel.
|
|
||||||
*
|
|
||||||
* @function Volume:get
|
|
||||||
* @tparam number index The channel index
|
|
||||||
* @treturn number The channel's volume
|
|
||||||
*/
|
|
||||||
int volume_get(lua_State*);
|
|
||||||
|
|
||||||
/** Increases the volume by the given amount.
|
|
||||||
*
|
|
||||||
* The proportions between the channels are kept.
|
|
||||||
*
|
|
||||||
* @function Volume:inc
|
|
||||||
* @tparam number value the value to increase by.
|
|
||||||
*/
|
|
||||||
int volume_inc(lua_State*);
|
|
||||||
|
|
||||||
/** Returns `true` when all channels are muted.
|
|
||||||
*
|
|
||||||
* @function Volume:is_muted
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int volume_is_muted(lua_State*);
|
|
||||||
|
|
||||||
/** Returns `true` when all channels are on normal level.
|
|
||||||
*
|
|
||||||
* @function Volume:is_norm
|
|
||||||
* @treturn boolean
|
|
||||||
*/
|
|
||||||
int volume_is_norm(lua_State*);
|
|
||||||
|
|
||||||
/** Returns the maximum volume out of all channels.
|
|
||||||
*
|
|
||||||
* @function Volume:max
|
|
||||||
* @treturn number
|
|
||||||
*/
|
|
||||||
int volume_max(lua_State*);
|
|
||||||
|
|
||||||
/** Returns the minimum volume out of all channels.
|
|
||||||
*
|
|
||||||
* @function Volume:min
|
|
||||||
* @treturn number
|
|
||||||
*/
|
|
||||||
int volume_min(lua_State*);
|
|
||||||
|
|
||||||
/** Multiplies the volume by the given amount.
|
|
||||||
*
|
|
||||||
* The value to multiply with may either be a scalar, that's applied to all channels,
|
|
||||||
* or another instance of @{Volume}, which would be applied channel by channel.
|
|
||||||
*
|
|
||||||
* It is possible to multiply a @{Volume} by itself.
|
|
||||||
*
|
|
||||||
* This is only valid for software volumes.
|
|
||||||
*
|
|
||||||
* @function Volume:multiply
|
|
||||||
* @tparam number|Volume value
|
|
||||||
*/
|
|
||||||
int volume_multiply(lua_State*);
|
|
||||||
|
|
||||||
/** Mutes all channels.
|
|
||||||
*
|
|
||||||
* @function Volume:mute
|
|
||||||
*/
|
|
||||||
int volume_mute(lua_State*);
|
|
||||||
|
|
||||||
/** Resets all channels to normal volume.
|
|
||||||
*
|
|
||||||
* @function Volume:reset
|
|
||||||
*/
|
|
||||||
int volume_reset(lua_State*);
|
|
||||||
|
|
||||||
/** Scales all channels to the passed amount.
|
|
||||||
*
|
|
||||||
* This adjust all channel volumes so that the maximum between them equals the given value, while
|
|
||||||
* keeping proportions between channels the same.
|
|
||||||
*
|
|
||||||
* @function Volume:scale
|
|
||||||
* @tparam number value The value to scale to.
|
|
||||||
*/
|
|
||||||
int volume_scale(lua_State*);
|
|
||||||
|
|
||||||
/** Sets a channel to the given value.
|
|
||||||
*
|
|
||||||
* @function Volume:set
|
|
||||||
* @tparam number index The channel index.
|
|
||||||
* @tparam number value The volume to set to.
|
|
||||||
*/
|
|
||||||
int volume_set(lua_State*);
|
|
||||||
|
|
||||||
/** Sets a number of channels to the given volume value.
|
|
||||||
*
|
|
||||||
* @function Volume:set_channels
|
|
||||||
* @tparam number channels The number of channels to set
|
|
||||||
* @tparam number value The volume to set to.
|
|
||||||
*/
|
|
||||||
int volume_set_channels(lua_State*);
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg volume_f[] = {
|
|
||||||
{"avg", volume_avg },
|
|
||||||
{ "channels_equal_to", volume_channels_equal_to},
|
|
||||||
{ "dec", volume_dec },
|
|
||||||
{ "divide", volume_divide },
|
|
||||||
{ "get", volume_get },
|
|
||||||
{ "inc", volume_inc },
|
|
||||||
{ "is_muted", volume_is_muted },
|
|
||||||
{ "is_norm", volume_is_norm },
|
|
||||||
{ "is_valid", volume_is_valid },
|
|
||||||
{ "max", volume_max },
|
|
||||||
{ "min", volume_min },
|
|
||||||
{ "multiply", volume_multiply },
|
|
||||||
{ "mute", volume_mute },
|
|
||||||
{ "reset", volume_reset },
|
|
||||||
{ "scale", volume_scale },
|
|
||||||
{ "set", volume_set },
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// We don't add `__index` here, as it needs to be handled specially,
|
|
||||||
// to allow indexing both by channel index as well as accessing the methods above.
|
|
||||||
static const struct luaL_Reg volume_mt[] = {
|
|
||||||
{"__len", volume__len },
|
|
||||||
{ "__tostring", volume__tostring},
|
|
||||||
{ "__eq", volume__eq },
|
|
||||||
{ "__newindex", volume__newindex},
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static const struct luaL_Reg volume_lib[] = {
|
|
||||||
{"new", volume_new },
|
|
||||||
{ "from_dB", volume_from_dB },
|
|
||||||
{ "to_dB", volume_to_dB },
|
|
||||||
{ "from_linear", volume_from_linear},
|
|
||||||
{ "to_linear", volume_to_linear },
|
|
||||||
{ "is_valid", volume_is_valid },
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // volume_h_INCLUDED
|
|
||||||
|
|
8
test.lua
Normal file
8
test.lua
Normal file
|
@ -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()))
|
|
@ -14,7 +14,7 @@ run() {
|
||||||
"$@"
|
"$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
find src -iname '*.lua' -or -iname '*.c' -or -iname '*.h' -not -path '*/internal/*' | while read -r f; do
|
find src -iname '*.lua' -or -iname '*.c' -not -path '*/internal/*' | while read -r f; do
|
||||||
mkdir -p "$(dirname "$OUT/$f")"
|
mkdir -p "$(dirname "$OUT/$f")"
|
||||||
run "$LUA" ./tools/preprocessor.lua "$f" "$OUT/$f"
|
run "$LUA" ./tools/preprocessor.lua "$f" "$OUT/$f"
|
||||||
done
|
done
|
||||||
|
|
Loading…
Add table
Reference in a new issue