~schnouki/pustule

7aae08ce8e322c9d2b0f4e394288fba855c186db — Thomas Jost 7 years ago aaa19b6
Split C source
5 files changed, 207 insertions(+), 116 deletions(-)

M Makefile
A guile.c
R pustule.c => main.c
A pulse.c
A pustule.h
M Makefile => Makefile +2 -2
@@ 8,13 8,13 @@ LDFLAGS = -O2
DEPS_CFLAGS  = -Wall `pkg-config --cflags guile-2.0 libpulse`
DEPS_LDFLAGS = -Wall `pkg-config --libs guile-2.0 libpulse`

pustule: pustule.o
pustule: main.o pulse.o guile.o
	$(CC) -o $@ $^ $(LDFLAGS) $(DEPS_LDFLAGS)

%.o: %.c
	$(CC) -c -o $@ $< $(CFLAGS) $(DEPS_CFLAGS)

pustule.o: docopt.c
main.o: docopt.c

docopt.c: pustule.docopt docopt/docopt_c.py
	$(PYTHON) docopt/docopt_c.py -o $@ $<

A guile.c => guile.c +53 -0
@@ 0,0 1,53 @@
/**
 * pustule
 * Copyright 2014 Thomas Jost <schnouki@schnouki.net>
 *
 * This file is part of pustule.
 *
 * pustule is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * pustule is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * pustule. If not, see <http://www.gnu.org/licenses/>.
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libguile.h>
#include <pulse/pulseaudio.h>

#include "pustule.h"

/* Global handlers */
SCM sink_added_callback = NULL;
SCM sink_removed_callback = NULL;

/* Set the handlers functions */
SCM pustule_set_handlers(SCM sink_added, SCM sink_removed) {
    sink_added_callback = sink_added;
    sink_removed_callback = sink_removed;
    return SCM_BOOL_T;
}

/* Set volume for the specified sink */
SCM pustule_set_volume(SCM idx, SCM volume) {
    uint32_t idx_ = scm_to_uint32(idx);
    double volume_ = scm_to_double(volume);

    pa_cvolume cvol;
    pa_cvolume_init(&cvol);
    pa_volume_t new_vol = (pa_volume_t) (volume_ * PA_VOLUME_NORM);
    pa_cvolume_set(&cvol, 1, new_vol);
    pa_operation* op = pa_context_set_sink_input_volume(pactx, idx_, &cvol, NULL, NULL);
    pa_operation_unref(op);
    return volume;
}

R pustule.c => main.c +20 -114
@@ 2,18 2,19 @@
 * pustule
 * Copyright 2014 Thomas Jost <schnouki@schnouki.net>
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * This file is part of pustule.
 *
 * pustule is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * pustule is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 * pustule. If not, see <http://www.gnu.org/licenses/>.
 */

#define _GNU_SOURCE


@@ 24,22 25,7 @@
#include <libguile.h>
#include <pulse/pulseaudio.h>

#define PUSTULE_VERSION "pustule 0.1"

/* PulseAudio callbacks */
static void sink_event_callback(pa_context* ctx, pa_subscription_event_type_t t, uint32_t idx, void* userdata);
static void sink_info_callback(pa_context* ctx, const pa_sink_input_info* i, int eol, void* userdata);
static void state_callback(pa_context* ctx, void* userdata);

/* Guile functions */
static void* inner_main(void* data);
static SCM pustule_set_handlers(SCM sink_added, SCM sink_removed);
static SCM pustule_set_volume(SCM idx, SCM volume);

/* Global variables */
static pa_context* pactx = NULL;
static SCM sink_added_callback = NULL;
static SCM sink_removed_callback = NULL;
#include "pustule.h"

struct pustule_exported_func {
    const char* name;


@@ 63,7 49,7 @@ struct pustule_exported_func {

/* Get the name of the default configuration file, using environment
   variables */
char* get_default_config_file() {
static char* get_default_config_file() {
    char* config_dir;
    char* config_file;
    char* default_dir = getenv("XDG_CONFIG_HOME");


@@ 89,19 75,7 @@ char* get_default_config_file() {
#pragma GCC diagnostic pop
#pragma GCC diagnostic pop

/* Initialization */
int main(int argc, char** argv) {
    /* Parse command-line arguments */
    DocoptArgs args = docopt(argc, argv, 1, PUSTULE_VERSION);
    if (!args.config)
        args.config = get_default_config_file();

    /* Initialize Guile */
    scm_with_guile(inner_main, &args);
    return 0;
}

void* inner_main(void* data) {
static void* inner_main(void* data) {
    DocoptArgs* args = (DocoptArgs*) data;

    /* Tell Guile about our functions, and document them */


@@ 155,82 129,14 @@ void* inner_main(void* data) {
    return NULL;
}

/* Set the handlers functions */
SCM pustule_set_handlers(SCM sink_added, SCM sink_removed) {
    sink_added_callback = sink_added;
    sink_removed_callback = sink_removed;
    return SCM_BOOL_T;
}

/* Set volume for the specified sink */
SCM pustule_set_volume(SCM idx, SCM volume) {
    uint32_t idx_ = scm_to_uint32(idx);
    double volume_ = scm_to_double(volume);

    pa_cvolume cvol;
    pa_cvolume_init(&cvol);
    pa_volume_t new_vol = (pa_volume_t) (volume_ * PA_VOLUME_NORM);
    pa_cvolume_set(&cvol, 1, new_vol);
    pa_operation* op = pa_context_set_sink_input_volume(pactx, idx_, &cvol, NULL, NULL);
    pa_operation_unref(op);
    return volume;
}

/* PulseAudio state changed -- query for infos and subscribe to new events */
void state_callback(pa_context* ctx, void* userdata) {
    pa_context_state_t state = pa_context_get_state(ctx);
    if (state == PA_CONTEXT_READY) {
        // Get infos about all current sinks
        pa_context_get_sink_input_info_list(ctx, sink_info_callback, NULL);

        // Subscribe to new sinks events
        pa_context_set_subscribe_callback(ctx, sink_event_callback, NULL);
        pa_context_subscribe(ctx, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL);
    }
}

/* Event handler -- dispatch Scheme events */
void sink_event_callback(pa_context* ctx, pa_subscription_event_type_t t, uint32_t idx, void* userdata) {
    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
        // Sink added: query info about it
        pa_context_get_sink_input_info(ctx, idx, sink_info_callback, NULL);
    }
    else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
        // Sink removed: directly call the corresponding callback
        if (scm_is_true(sink_removed_callback)) {
            SCM my_idx = scm_from_uint32(idx);
            scm_call_1(sink_removed_callback, my_idx);
        }
    }
}
/* Initialization */
int main(int argc, char** argv) {
    /* Parse command-line arguments */
    DocoptArgs args = docopt(argc, argv, 1, PUSTULE_VERSION);
    if (!args.config)
        args.config = get_default_config_file();

/* New sink available (or infos about an existing sink): call the added callback */
void sink_info_callback(pa_context* ctx, const pa_sink_input_info* i, int eol, void* userdata) {
    if (!i || !scm_is_true(sink_added_callback)) {
        return;
    }
    SCM info = SCM_EOL;
    info = scm_acons(scm_from_utf8_string("name"),            scm_from_utf8_string(i->name),                info);
    info = scm_acons(scm_from_utf8_string("driver"),          scm_from_utf8_string(i->driver),              info);
    info = scm_acons(scm_from_utf8_string("muted"),           i->mute ? SCM_BOOL_T : SCM_BOOL_F,           info);
    info = scm_acons(scm_from_utf8_string("volume_writable"), i->volume_writable ? SCM_BOOL_T : SCM_BOOL_F, info);
    if (i->has_volume) {
        double vol = ((double) pa_cvolume_avg(&(i->volume))) / PA_VOLUME_NORM;
        info = scm_acons(scm_from_utf8_string("volume"), scm_from_double(vol), info);

        vol = pa_sw_volume_to_dB(pa_cvolume_avg(&(i->volume)));
        info = scm_acons(scm_from_utf8_string("volume_db"), scm_from_double(vol), info);
    }
    else {
        info = scm_acons(scm_from_utf8_string("volume"), SCM_BOOL_F, info);
    }
    if (i->proplist && !pa_proplist_isempty(i->proplist)) {
        void* state = NULL;
        const char* key;
        while ((key = pa_proplist_iterate(i->proplist, &state)) != NULL) {
            const char* value = pa_proplist_gets(i->proplist, key);
            info = scm_acons(scm_from_utf8_string(key), scm_from_utf8_string(value), info);
        }
    }
    scm_call_2(sink_added_callback, scm_from_uint32(i->index), info);
    /* Initialize Guile */
    scm_with_guile(inner_main, &args);
    return 0;
}

A pulse.c => pulse.c +90 -0
@@ 0,0 1,90 @@
/**
 * pustule
 * Copyright 2014 Thomas Jost <schnouki@schnouki.net>
 *
 * This file is part of pustule.
 *
 * pustule is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * pustule is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * pustule. If not, see <http://www.gnu.org/licenses/>.
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libguile.h>
#include <pulse/pulseaudio.h>

#include "pustule.h"

/* Global PulseAudio context */
pa_context* pactx = NULL;

/* PulseAudio state changed -- query for infos and subscribe to new events */
void state_callback(pa_context* ctx, void* userdata) {
    pa_context_state_t state = pa_context_get_state(ctx);
    if (state == PA_CONTEXT_READY) {
        // Get infos about all current sinks
        pa_context_get_sink_input_info_list(ctx, sink_info_callback, NULL);

        // Subscribe to new sinks events
        pa_context_set_subscribe_callback(ctx, sink_event_callback, NULL);
        pa_context_subscribe(ctx, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL);
    }
}

/* Event handler -- dispatch Scheme events */
void sink_event_callback(pa_context* ctx, pa_subscription_event_type_t t, uint32_t idx, void* userdata) {
    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
        // Sink added: query info about it
        pa_context_get_sink_input_info(ctx, idx, sink_info_callback, NULL);
    }
    else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
        // Sink removed: directly call the corresponding callback
        if (scm_is_true(sink_removed_callback)) {
            SCM my_idx = scm_from_uint32(idx);
            scm_call_1(sink_removed_callback, my_idx);
        }
    }
}

/* New sink available (or infos about an existing sink): call the added callback */
void sink_info_callback(pa_context* ctx, const pa_sink_input_info* i, int eol, void* userdata) {
    if (!i || !scm_is_true(sink_added_callback)) {
        return;
    }
    SCM info = SCM_EOL;
    info = scm_acons(scm_from_utf8_string("name"),            scm_from_utf8_string(i->name),                info);
    info = scm_acons(scm_from_utf8_string("driver"),          scm_from_utf8_string(i->driver),              info);
    info = scm_acons(scm_from_utf8_string("muted"),           i->mute ? SCM_BOOL_T : SCM_BOOL_F,           info);
    info = scm_acons(scm_from_utf8_string("volume_writable"), i->volume_writable ? SCM_BOOL_T : SCM_BOOL_F, info);
    if (i->has_volume) {
        double vol = ((double) pa_cvolume_avg(&(i->volume))) / PA_VOLUME_NORM;
        info = scm_acons(scm_from_utf8_string("volume"), scm_from_double(vol), info);

        vol = pa_sw_volume_to_dB(pa_cvolume_avg(&(i->volume)));
        info = scm_acons(scm_from_utf8_string("volume_db"), scm_from_double(vol), info);
    }
    else {
        info = scm_acons(scm_from_utf8_string("volume"), SCM_BOOL_F, info);
    }
    if (i->proplist && !pa_proplist_isempty(i->proplist)) {
        void* state = NULL;
        const char* key;
        while ((key = pa_proplist_iterate(i->proplist, &state)) != NULL) {
            const char* value = pa_proplist_gets(i->proplist, key);
            info = scm_acons(scm_from_utf8_string(key), scm_from_utf8_string(value), info);
        }
    }
    scm_call_2(sink_added_callback, scm_from_uint32(i->index), info);
}

A pustule.h => pustule.h +42 -0
@@ 0,0 1,42 @@
/**
 * pustule
 * Copyright 2014 Thomas Jost <schnouki@schnouki.net>
 *
 * This file is part of pustule.
 *
 * pustule is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * pustule is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * pustule. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef PUSTULE_H
#define PUSTULE_H

#define PUSTULE_VERSION "pustule 0.1"

#include <libguile.h>
#include <pulse/pulseaudio.h>

/* PulseAudio callbacks */
void sink_event_callback(pa_context* ctx, pa_subscription_event_type_t t, uint32_t idx, void* userdata);
void sink_info_callback(pa_context* ctx, const pa_sink_input_info* i, int eol, void* userdata);
void state_callback(pa_context* ctx, void* userdata);

/* Guile functions */
SCM pustule_set_handlers(SCM sink_added, SCM sink_removed);
SCM pustule_set_volume(SCM idx, SCM volume);

/* Global variables */
extern pa_context* pactx;
extern SCM sink_added_callback;
extern SCM sink_removed_callback;

#endif