summaryrefslogtreecommitdiff
path: root/libs/backends/jack/weak_libjack.c
blob: 10f2fdf6e256b083c05c5145db3ebb3a43223cc8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/* runtime/weak dynamic JACK linking
 *
 * (C) 2014 Robin Gareus <robin@gareus.org>
 *
 * 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 Foundation; either version 2 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.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "weak_libjack.h"

#ifndef USE_WEAK_JACK

int have_libjack (void) {
	return 0;
}

#else

#include <stdio.h>
#include <string.h>
#include <assert.h>

#ifdef PLATFORM_WINDOWS
#include <windows.h>
#else
#include <dlfcn.h>
#endif

static void* lib_open(const char* const so) {
#ifdef PLATFORM_WINDOWS
	return (void*) LoadLibraryA(so);
#else
	return dlopen(so, RTLD_NOW|RTLD_LOCAL);
#endif
}

static void* lib_symbol(void* const lib, const char* const sym) {
#ifdef PLATFORM_WINDOWS
	return (void*) GetProcAddress((HMODULE)lib, sym);
#else
	return dlsym(lib, sym);
#endif
}

#ifdef COMPILER_MSVC
typedef void * pvoid_t;
#define MAPSYM(SYM, FAIL) _j._ ## SYM = (func_t)lib_symbol(lib, "jack_" # SYM); \
	if (!_j._ ## SYM) err |= FAIL;
#elif defined NDEBUG
typedef void * __attribute__ ((__may_alias__)) pvoid_t;
#define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \
	if (!_j._ ## SYM) err |= FAIL;
#else
typedef void * __attribute__ ((__may_alias__)) pvoid_t;
#define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \
	if (!_j._ ## SYM) { \
		if (FAIL) { \
			fprintf(stderr, "*** WEAK-JACK: required symbol 'jack_%s' was not found\n", "" # SYM); \
		} \
		err |= FAIL; \
	}
#endif

typedef void (* func_t) (void);

/* function pointers to the real jack API */
static struct WeakJack {
	func_t _client_open; // special case due to varargs

#define JCFUN(ERR, RTYPE, NAME, RVAL)              func_t _ ## NAME ;
#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL)   func_t _ ## NAME ;
#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE)   func_t _ ## NAME ;
#define JVFUN(ERR, NAME, DEF, ARGS, CODE)          func_t _ ## NAME ;

#include "weak_libjack.def"

#undef JCFUN
#undef JPFUN
#undef JXFUN
#undef JVFUN
} _j;

static int _status = -1;

__attribute__((constructor))
static void init_weak_jack(void)
{
	void* lib;
	int err = 0;
#ifndef NDEBUG
	fprintf(stderr, "*** WEAK-JACK: initializing\n");
#endif

	memset(&_j, 0, sizeof(_j));

#ifdef __APPLE__
	lib = lib_open("libjack.dylib");
	if (!lib) {
		lib = lib_open("/usr/local/lib/libjack.dylib");
	}
#elif (defined PLATFORM_WINDOWS)
# if ( defined(__x86_64__) || defined(_M_X64) )
	lib = lib_open("libjack64.dll");
# else
	lib = lib_open("libjack.dll");
# endif
#else
	lib = lib_open("libjack.so.0");
#endif
	if (!lib) {
#ifndef NDEBUG
		fprintf(stderr, "*** WEAK-JACK: libjack was not found\n");
#endif
		_status = -2;
		return;
	}

	/* found library, now lookup functions */
	MAPSYM(client_open, 2)

#define JCFUN(ERR, RTYPE, NAME, RVAL)             MAPSYM(NAME, ERR)
#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL)  MAPSYM(NAME, ERR)
#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE)  MAPSYM(NAME, ERR)
#define JVFUN(ERR, NAME, DEF, ARGS, CODE)         MAPSYM(NAME, ERR)

#include "weak_libjack.def"

#undef JCFUN
#undef JPFUN
#undef JXFUN
#undef JVFUN

	/* if a required symbol is not found, disable JACK completly */
	if (err) {
		_j._client_open = NULL;
	}
	_status = err;
#ifndef NDEBUG
	fprintf(stderr, "*** WEAK-JACK: %s. (%d)\n", err ? "jack is not available" : "OK", _status);
#endif
}

int have_libjack (void) {
	if (_status == -1) {
		init_weak_jack();
	}
	return _status;
}

/*******************************************************************************
 * helper macros
 */

#if defined(__GNUC__) && (__GNUC__ > 2) && !defined(NDEBUG)
#define likely(expr) (__builtin_expect (!!(expr), 1))
#else
#define likely(expr) (expr)
#endif

#ifndef NDEBUG
# define WJACK_WARNING(NAME) \
	fprintf(stderr, "*** WEAK-JACK: function 'jack_%s' ignored\n", "" # NAME);
#else
# define WJACK_WARNING(NAME) ;
#endif

/******************************************************************************
 * JACK API wrapper functions.
 *
 * if a function pointer is set in the static struct WeakJack _j,
 * the function is called directly.
 * Otherwise a dummy NOOP implementation is provided.
 * The latter is mainly for compile-time warnings.
 *
 * If libjack is not found, jack_client_open() will fail.
 * In that case the application should not call any other libjack
 * functions. Hence a real implementation is not needed.
 * (jack ringbuffer may be an exception for some apps)
 */

/* dedicated support for jack_client_open(,..) variable arg function macro */
func_t WJACK_get_client_open(void) {
	if (_status == -1) {
		init_weak_jack();
	}
	return _j._client_open;
}

/* callback to set status */
jack_client_t * WJACK_no_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...) {
	WJACK_WARNING(client_open);
	if (status) { *status = JackFailure; }
	return NULL;
}

/*******************************************************************************
 * Macros to wrap jack API
 */

/* abstraction for jack_client functions
 *  rtype jack_function_name (jack_client_t *client) { return rval; }
 */
#define JCFUN(ERR, RTYPE, NAME, RVAL) \
	RTYPE WJACK_ ## NAME (jack_client_t *client) { \
		if likely(_j._ ## NAME) { \
			return ((RTYPE (*)(jack_client_t *client)) _j._ ## NAME)(client); \
		} else { \
			WJACK_WARNING(NAME) \
			return RVAL; \
		} \
	}

/* abstraction for NOOP functions with return value
 *  rtype jack_function_name (ARGS) { return rval; }
 */
#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) \
	RTYPE WJACK_ ## NAME DEF { \
		if likely(_j._ ## NAME) { \
			return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \
		} else { \
			WJACK_WARNING(NAME) \
			return RVAL; \
		} \
	}

/* abstraction for functions that need custom code.
 * e.g. functions with return-value-pointer args,
 * use CODE to initialize value
 *
 *  rtype jack_function_name (ARGS) { CODE }
 */
#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) \
	RTYPE WJACK_ ## NAME DEF { \
		if likely(_j._ ## NAME) { \
			return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \
		} else { \
			WJACK_WARNING(NAME) \
			CODE \
		} \
	}

/* abstraction for void functions with return-value-pointer args
 *  void jack_function_name (ARGS) { CODE }
 */
#define JVFUN(ERR, NAME, DEF, ARGS, CODE) \
	void WJACK_ ## NAME DEF { \
		if likely(_j._ ## NAME) { \
			((void (*)DEF) _j._ ## NAME) ARGS; \
		} else { \
			WJACK_WARNING(NAME) \
			CODE \
		} \
	}

#include "weak_libjack.def"

#undef JCFUN
#undef JPFUN
#undef JXFUN
#undef JVFUN

#endif // end USE_WEAK_JACK