Compare commits
	
		
			6 Commits
		
	
	
		
			d71c47f8ce
			...
			782a9a5977
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						782a9a5977
	
				 | 
					
					
						|||
| 
						
						
							
						
						34f86651f6
	
				 | 
					
					
						|||
| 
						
						
							
						
						5dec46df2c
	
				 | 
					
					
						|||
| 
						
						
							
						
						1b9d89cab3
	
				 | 
					
					
						|||
| 
						
						
							
						
						a3ad5e7751
	
				 | 
					
					
						|||
| 
						
						
							
						
						960420df3e
	
				 | 
					
					
						
							
								
								
									
										32
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					# clang-format is fairly limited, and these rules are approximate:
 | 
				
			||||||
 | 
					#  - array initializers can get terribly mangled with clang-format 12.0,
 | 
				
			||||||
 | 
					#  - sometimes it still aligns with space characters,
 | 
				
			||||||
 | 
					#  - struct name NL { NL ... NL } NL name; is unachievable.
 | 
				
			||||||
 | 
					BasedOnStyle: GNU
 | 
				
			||||||
 | 
					ColumnLimit: 80
 | 
				
			||||||
 | 
					IndentWidth: 4
 | 
				
			||||||
 | 
					TabWidth: 4
 | 
				
			||||||
 | 
					UseTab: ForContinuationAndIndentation
 | 
				
			||||||
 | 
					BreakBeforeBraces: Allman
 | 
				
			||||||
 | 
					SpaceAfterCStyleCast: true
 | 
				
			||||||
 | 
					AlignAfterOpenBracket: DontAlign
 | 
				
			||||||
 | 
					AlignOperands: DontAlign
 | 
				
			||||||
 | 
					AlignConsecutiveMacros: Consecutive
 | 
				
			||||||
 | 
					AllowAllArgumentsOnNextLine: false
 | 
				
			||||||
 | 
					AllowAllParametersOfDeclarationOnNextLine: false
 | 
				
			||||||
 | 
					IndentGotoLabels: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# IncludeCategories has some potential, but it may also break the build.
 | 
				
			||||||
 | 
					# Note that the documentation says the value should be "Never".
 | 
				
			||||||
 | 
					SortIncludes: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# This is a compromise, it generally works out aesthetically better.
 | 
				
			||||||
 | 
					BinPackArguments: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Unfortunately, this can't be told to align to column 40 or so.
 | 
				
			||||||
 | 
					SpacesBeforeTrailingComments: 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# liberty-specific macro body wrappers.
 | 
				
			||||||
 | 
					MacroBlockBegin: "BLOCK_START"
 | 
				
			||||||
 | 
					MacroBlockEnd: "BLOCK_END"
 | 
				
			||||||
 | 
					ForEachMacros: ["LIST_FOR_EACH"]
 | 
				
			||||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -7,3 +7,5 @@
 | 
				
			|||||||
/liberty.files
 | 
					/liberty.files
 | 
				
			||||||
/liberty.creator*
 | 
					/liberty.creator*
 | 
				
			||||||
/liberty.includes
 | 
					/liberty.includes
 | 
				
			||||||
 | 
					/liberty.cflags
 | 
				
			||||||
 | 
					/liberty.cxxflags
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
project (liberty C)
 | 
					project (liberty C)
 | 
				
			||||||
cmake_minimum_required (VERSION 2.8.5)
 | 
					cmake_minimum_required (VERSION 2.8.12)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Moar warnings
 | 
					# Moar warnings
 | 
				
			||||||
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
 | 
					if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
 | 
				
			||||||
@@ -36,7 +36,17 @@ endforeach ()
 | 
				
			|||||||
# Build some unit tests
 | 
					# Build some unit tests
 | 
				
			||||||
include_directories (${PROJECT_SOURCE_DIR})
 | 
					include_directories (${PROJECT_SOURCE_DIR})
 | 
				
			||||||
enable_testing ()
 | 
					enable_testing ()
 | 
				
			||||||
foreach (name liberty proto)
 | 
					set (tests liberty proto)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pkg_check_modules (libpulse libpulse)
 | 
				
			||||||
 | 
					if (libpulse_FOUND)
 | 
				
			||||||
 | 
						list (APPEND tests pulse)
 | 
				
			||||||
 | 
						list (APPEND common_libraries ${libpulse_LIBRARIES})
 | 
				
			||||||
 | 
						include_directories (${libpulse_INCLUDE_DIRS})
 | 
				
			||||||
 | 
						link_directories (${libpulse_LIBRARY_DIRS})
 | 
				
			||||||
 | 
					endif ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					foreach (name ${tests})
 | 
				
			||||||
	add_executable (test-${name} tests/${name}.c ${common_sources})
 | 
						add_executable (test-${name} tests/${name}.c ${common_sources})
 | 
				
			||||||
	add_threads (test-${name})
 | 
						add_threads (test-${name})
 | 
				
			||||||
	target_link_libraries (test-${name} ${common_libraries})
 | 
						target_link_libraries (test-${name} ${common_libraries})
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										348
									
								
								liberty-pulse.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								liberty-pulse.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,348 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * liberty-pulse.c: PulseAudio mainloop abstraction
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (c) 2016 - 2021, Přemysl Eric Janouch <p@janouch.name>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission to use, copy, modify, and/or distribute this software for any
 | 
				
			||||||
 | 
					 * purpose with or without fee is hereby granted.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
				
			||||||
 | 
					 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
				
			||||||
 | 
					 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 | 
				
			||||||
 | 
					 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
				
			||||||
 | 
					 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 | 
				
			||||||
 | 
					 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 | 
				
			||||||
 | 
					 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pulse/mainloop.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- PulseAudio mainloop abstraction -----------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pa_io_event
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						LIST_HEADER (pa_io_event)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pa_mainloop_api *api;               ///< Parent structure
 | 
				
			||||||
 | 
						struct poller_fd fd;                ///< Underlying FD event
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pa_io_event_cb_t dispatch;          ///< Dispatcher
 | 
				
			||||||
 | 
						pa_io_event_destroy_cb_t free;      ///< Destroyer
 | 
				
			||||||
 | 
						void *user_data;                    ///< User data
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pa_time_event
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						LIST_HEADER (pa_time_event)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pa_mainloop_api *api;               ///< Parent structure
 | 
				
			||||||
 | 
						struct poller_timer timer;          ///< Underlying timer event
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pa_time_event_cb_t dispatch;        ///< Dispatcher
 | 
				
			||||||
 | 
						pa_time_event_destroy_cb_t free;    ///< Destroyer
 | 
				
			||||||
 | 
						void *user_data;                    ///< User data
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pa_defer_event
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						LIST_HEADER (pa_defer_event)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pa_mainloop_api *api;               ///< Parent structure
 | 
				
			||||||
 | 
						struct poller_idle idle;            ///< Underlying idle event
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pa_defer_event_cb_t dispatch;       ///< Dispatcher
 | 
				
			||||||
 | 
						pa_defer_event_destroy_cb_t free;   ///< Destroyer
 | 
				
			||||||
 | 
						void *user_data;                    ///< User data
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct poller_pa
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct poller *poller;              ///< The underlying event loop
 | 
				
			||||||
 | 
						pa_io_event *io_list;               ///< I/O events
 | 
				
			||||||
 | 
						pa_time_event *time_list;           ///< Timer events
 | 
				
			||||||
 | 
						pa_defer_event *defer_list;         ///< Deferred events
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static short
 | 
				
			||||||
 | 
					poller_pa_flags_to_events (pa_io_event_flags_t flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						short result = 0;
 | 
				
			||||||
 | 
						if (flags & PA_IO_EVENT_ERROR)   result |= POLLERR;
 | 
				
			||||||
 | 
						if (flags & PA_IO_EVENT_HANGUP)  result |= POLLHUP;
 | 
				
			||||||
 | 
						if (flags & PA_IO_EVENT_INPUT)   result |= POLLIN;
 | 
				
			||||||
 | 
						if (flags & PA_IO_EVENT_OUTPUT)  result |= POLLOUT;
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static pa_io_event_flags_t
 | 
				
			||||||
 | 
					poller_pa_events_to_flags (short events)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pa_io_event_flags_t result = 0;
 | 
				
			||||||
 | 
						if (events & POLLERR)  result |= PA_IO_EVENT_ERROR;
 | 
				
			||||||
 | 
						if (events & POLLHUP)  result |= PA_IO_EVENT_HANGUP;
 | 
				
			||||||
 | 
						if (events & POLLIN)   result |= PA_IO_EVENT_INPUT;
 | 
				
			||||||
 | 
						if (events & POLLOUT)  result |= PA_IO_EVENT_OUTPUT;
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct timeval
 | 
				
			||||||
 | 
					poller_pa_get_current_time (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct timeval tv;
 | 
				
			||||||
 | 
					#ifdef _POSIX_TIMERS
 | 
				
			||||||
 | 
						struct timespec tp;
 | 
				
			||||||
 | 
						hard_assert (clock_gettime (CLOCK_REALTIME, &tp) != -1);
 | 
				
			||||||
 | 
						tv.tv_sec = tp.tv_sec;
 | 
				
			||||||
 | 
						tv.tv_usec = tp.tv_nsec / 1000;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						gettimeofday (&tv, NULL);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						return tv;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_io_dispatcher (const struct pollfd *pfd, void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pa_io_event *self = user_data;
 | 
				
			||||||
 | 
						self->dispatch (self->api, self,
 | 
				
			||||||
 | 
							pfd->fd, poller_pa_events_to_flags (pfd->revents), self->user_data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_io_enable (pa_io_event *self, pa_io_event_flags_t events)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct poller_fd *fd = &self->fd;
 | 
				
			||||||
 | 
						if (events)
 | 
				
			||||||
 | 
							poller_fd_set (fd, poller_pa_flags_to_events (events));
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							poller_fd_reset (fd);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static pa_io_event *
 | 
				
			||||||
 | 
					poller_pa_io_new (pa_mainloop_api *api, int fd_, pa_io_event_flags_t events,
 | 
				
			||||||
 | 
						pa_io_event_cb_t cb, void *userdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pa_io_event *self = xcalloc (1, sizeof *self);
 | 
				
			||||||
 | 
						self->api = api;
 | 
				
			||||||
 | 
						self->dispatch = cb;
 | 
				
			||||||
 | 
						self->user_data = userdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct poller_pa *data = api->userdata;
 | 
				
			||||||
 | 
						self->fd = poller_fd_make (data->poller, fd_);
 | 
				
			||||||
 | 
						self->fd.user_data = self;
 | 
				
			||||||
 | 
						self->fd.dispatcher = poller_pa_io_dispatcher;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// FIXME: under x2go PA tries to register twice for the same FD,
 | 
				
			||||||
 | 
						//   which fails with our curent poller implementation;
 | 
				
			||||||
 | 
						//   we could maintain a list of { poller_fd, listeners } structures;
 | 
				
			||||||
 | 
						//   or maybe we're doing something wrong, which is yet to be determined
 | 
				
			||||||
 | 
						poller_pa_io_enable (self, events);
 | 
				
			||||||
 | 
						LIST_PREPEND (data->io_list, self);
 | 
				
			||||||
 | 
						return self;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_io_free (pa_io_event *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (self->free)
 | 
				
			||||||
 | 
							self->free (self->api, self, self->user_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct poller_pa *data = self->api->userdata;
 | 
				
			||||||
 | 
						poller_fd_reset (&self->fd);
 | 
				
			||||||
 | 
						LIST_UNLINK (data->io_list, self);
 | 
				
			||||||
 | 
						free (self);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_io_set_destroy (pa_io_event *self, pa_io_event_destroy_cb_t cb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						self->free = cb;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_time_dispatcher (void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pa_time_event *self = user_data;
 | 
				
			||||||
 | 
						// XXX: the meaning of the time argument is undocumented,
 | 
				
			||||||
 | 
						//   so let's just put current Unix time in there
 | 
				
			||||||
 | 
						struct timeval now = poller_pa_get_current_time ();
 | 
				
			||||||
 | 
						self->dispatch (self->api, self, &now, self->user_data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_time_restart (pa_time_event *self, const struct timeval *tv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct poller_timer *timer = &self->timer;
 | 
				
			||||||
 | 
						if (tv)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							struct timeval now = poller_pa_get_current_time ();
 | 
				
			||||||
 | 
							poller_timer_set (timer,
 | 
				
			||||||
 | 
								(tv->tv_sec  - now.tv_sec)  * 1000 +
 | 
				
			||||||
 | 
								(tv->tv_usec - now.tv_usec) / 1000);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							poller_timer_reset (timer);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static pa_time_event *
 | 
				
			||||||
 | 
					poller_pa_time_new (pa_mainloop_api *api, const struct timeval *tv,
 | 
				
			||||||
 | 
						pa_time_event_cb_t cb, void *userdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pa_time_event *self = xcalloc (1, sizeof *self);
 | 
				
			||||||
 | 
						self->api = api;
 | 
				
			||||||
 | 
						self->dispatch = cb;
 | 
				
			||||||
 | 
						self->user_data = userdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct poller_pa *data = api->userdata;
 | 
				
			||||||
 | 
						self->timer = poller_timer_make (data->poller);
 | 
				
			||||||
 | 
						self->timer.user_data = self;
 | 
				
			||||||
 | 
						self->timer.dispatcher = poller_pa_time_dispatcher;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						poller_pa_time_restart (self, tv);
 | 
				
			||||||
 | 
						LIST_PREPEND (data->time_list, self);
 | 
				
			||||||
 | 
						return self;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_time_free (pa_time_event *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (self->free)
 | 
				
			||||||
 | 
							self->free (self->api, self, self->user_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct poller_pa *data = self->api->userdata;
 | 
				
			||||||
 | 
						poller_timer_reset (&self->timer);
 | 
				
			||||||
 | 
						LIST_UNLINK (data->time_list, self);
 | 
				
			||||||
 | 
						free (self);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_time_set_destroy (pa_time_event *self, pa_time_event_destroy_cb_t cb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						self->free = cb;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_defer_dispatcher (void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pa_defer_event *self = user_data;
 | 
				
			||||||
 | 
						self->dispatch (self->api, self, self->user_data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static pa_defer_event *
 | 
				
			||||||
 | 
					poller_pa_defer_new (pa_mainloop_api *api,
 | 
				
			||||||
 | 
						pa_defer_event_cb_t cb, void *userdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pa_defer_event *self = xcalloc (1, sizeof *self);
 | 
				
			||||||
 | 
						self->api = api;
 | 
				
			||||||
 | 
						self->dispatch = cb;
 | 
				
			||||||
 | 
						self->user_data = userdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct poller_pa *data = api->userdata;
 | 
				
			||||||
 | 
						self->idle = poller_idle_make (data->poller);
 | 
				
			||||||
 | 
						self->idle.user_data = self;
 | 
				
			||||||
 | 
						self->idle.dispatcher = poller_pa_defer_dispatcher;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						poller_idle_set (&self->idle);
 | 
				
			||||||
 | 
						LIST_PREPEND (data->defer_list, self);
 | 
				
			||||||
 | 
						return self;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_defer_enable (pa_defer_event *self, int enable)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct poller_idle *idle = &self->idle;
 | 
				
			||||||
 | 
						if (enable)
 | 
				
			||||||
 | 
							poller_idle_set (idle);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							poller_idle_reset (idle);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_defer_free (pa_defer_event *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (self->free)
 | 
				
			||||||
 | 
							self->free (self->api, self, self->user_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct poller_pa *data = self->api->userdata;
 | 
				
			||||||
 | 
						poller_idle_reset (&self->idle);
 | 
				
			||||||
 | 
						LIST_UNLINK (data->defer_list, self);
 | 
				
			||||||
 | 
						free (self);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_defer_set_destroy (pa_defer_event *self,
 | 
				
			||||||
 | 
						pa_defer_event_destroy_cb_t cb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						self->free = cb;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_quit (pa_mainloop_api *api, int retval)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(void) api;
 | 
				
			||||||
 | 
						(void) retval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This is not called from within libpulse
 | 
				
			||||||
 | 
						hard_assert (!"quitting the libpulse event loop is unimplemented");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct pa_mainloop_api g_poller_pa_template =
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						.io_new            = poller_pa_io_new,
 | 
				
			||||||
 | 
						.io_enable         = poller_pa_io_enable,
 | 
				
			||||||
 | 
						.io_free           = poller_pa_io_free,
 | 
				
			||||||
 | 
						.io_set_destroy    = poller_pa_io_set_destroy,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.time_new          = poller_pa_time_new,
 | 
				
			||||||
 | 
						.time_restart      = poller_pa_time_restart,
 | 
				
			||||||
 | 
						.time_free         = poller_pa_time_free,
 | 
				
			||||||
 | 
						.time_set_destroy  = poller_pa_time_set_destroy,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.defer_new         = poller_pa_defer_new,
 | 
				
			||||||
 | 
						.defer_enable      = poller_pa_defer_enable,
 | 
				
			||||||
 | 
						.defer_free        = poller_pa_defer_free,
 | 
				
			||||||
 | 
						.defer_set_destroy = poller_pa_defer_set_destroy,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.quit              = poller_pa_quit,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct pa_mainloop_api *
 | 
				
			||||||
 | 
					poller_pa_new (struct poller *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct poller_pa *data = xcalloc (1, sizeof *data);
 | 
				
			||||||
 | 
						data->poller = self;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct pa_mainloop_api *api = xmalloc (sizeof *api);
 | 
				
			||||||
 | 
						*api = g_poller_pa_template;
 | 
				
			||||||
 | 
						api->userdata = data;
 | 
				
			||||||
 | 
						return api;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					poller_pa_destroy (struct pa_mainloop_api *api)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct poller_pa *data = api->userdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LIST_FOR_EACH (pa_io_event, iter, data->io_list)
 | 
				
			||||||
 | 
							poller_pa_io_free (iter);
 | 
				
			||||||
 | 
						LIST_FOR_EACH (pa_time_event, iter, data->time_list)
 | 
				
			||||||
 | 
							poller_pa_time_free (iter);
 | 
				
			||||||
 | 
						LIST_FOR_EACH (pa_defer_event, iter, data->defer_list)
 | 
				
			||||||
 | 
							poller_pa_defer_free (iter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						free (data);
 | 
				
			||||||
 | 
						free (api);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								liberty.c
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								liberty.c
									
									
									
									
									
								
							@@ -1749,10 +1749,9 @@ poller_run (struct poller *self)
 | 
				
			|||||||
	self->revents_len = 0;
 | 
						self->revents_len = 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#elif defined (BSD)
 | 
					// Sort of similar to the epoll version.  Let's hope Darwin isn't broken,
 | 
				
			||||||
 | 
					// that'd mean reimplementing this in terms of select() just because of Crapple.
 | 
				
			||||||
// Mac OS X's kqueue is fatally broken, or so I've been told; leaving it out.
 | 
					#elif defined (BSD) || defined (__APPLE__)
 | 
				
			||||||
// Otherwise this is sort of similar to the epoll version.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <sys/types.h>
 | 
					#include <sys/types.h>
 | 
				
			||||||
#include <sys/event.h>
 | 
					#include <sys/event.h>
 | 
				
			||||||
@@ -3247,7 +3246,7 @@ get_xdg_config_dirs (struct strv *out)
 | 
				
			|||||||
	str_free (&config_home);
 | 
						str_free (&config_home);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const char *xdg_config_dirs;
 | 
						const char *xdg_config_dirs;
 | 
				
			||||||
	if (!(xdg_config_dirs = getenv ("XDG_CONFIG_DIRS")))
 | 
						if (!(xdg_config_dirs = getenv ("XDG_CONFIG_DIRS")) || !*xdg_config_dirs)
 | 
				
			||||||
		xdg_config_dirs = "/etc/xdg";
 | 
							xdg_config_dirs = "/etc/xdg";
 | 
				
			||||||
	cstr_split (xdg_config_dirs, ":", true, out);
 | 
						cstr_split (xdg_config_dirs, ":", true, out);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -3272,7 +3271,7 @@ get_xdg_data_dirs (struct strv *out)
 | 
				
			|||||||
	str_free (&data_home);
 | 
						str_free (&data_home);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const char *xdg_data_dirs;
 | 
						const char *xdg_data_dirs;
 | 
				
			||||||
	if (!(xdg_data_dirs = getenv ("XDG_DATA_DIRS")))
 | 
						if (!(xdg_data_dirs = getenv ("XDG_DATA_DIRS")) || !*xdg_data_dirs)
 | 
				
			||||||
		xdg_data_dirs = "/usr/local/share/:/usr/share/";
 | 
							xdg_data_dirs = "/usr/local/share/:/usr/share/";
 | 
				
			||||||
	cstr_split (xdg_data_dirs, ":", true, out);
 | 
						cstr_split (xdg_data_dirs, ":", true, out);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -4713,8 +4712,10 @@ config_item_write_string (struct str *output, const struct str *s)
 | 
				
			|||||||
		else if (c == '\t')  str_append (output, "\\t");
 | 
							else if (c == '\t')  str_append (output, "\\t");
 | 
				
			||||||
		else if (c == '\\')  str_append (output, "\\\\");
 | 
							else if (c == '\\')  str_append (output, "\\\\");
 | 
				
			||||||
		else if (c == '"')   str_append (output, "\\\"");
 | 
							else if (c == '"')   str_append (output, "\\\"");
 | 
				
			||||||
		else if (c < 32)     str_append_printf (output, "\\x%02x", c);
 | 
							else if (iscntrl_ascii (c))
 | 
				
			||||||
		else                 str_append_c      (output, c);
 | 
								str_append_printf (output, "\\x%02x", c);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								str_append_c (output, c);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	str_append_c (output, '"');
 | 
						str_append_c (output, '"');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -82,4 +82,3 @@ siphash (const unsigned char key[16], const unsigned char *m, size_t len)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return v0 ^ v1 ^ v2 ^ v3;
 | 
						return v0 ^ v1 ^ v2 ^ v3;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										127
									
								
								tests/pulse.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								tests/pulse.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * tests/pulse.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (c) 2021, Přemysl Eric Janouch <p@janouch.name>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission to use, copy, modify, and/or distribute this software for any
 | 
				
			||||||
 | 
					 * purpose with or without fee is hereby granted.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
				
			||||||
 | 
					 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
				
			||||||
 | 
					 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 | 
				
			||||||
 | 
					 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
				
			||||||
 | 
					 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 | 
				
			||||||
 | 
					 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 | 
				
			||||||
 | 
					 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PROGRAM_NAME "test"
 | 
				
			||||||
 | 
					#define PROGRAM_VERSION "0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LIBERTY_WANT_POLLER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../liberty.c"
 | 
				
			||||||
 | 
					#include "../liberty-pulse.c"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Tests -------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						EVENT_IO    =  1 << 0,
 | 
				
			||||||
 | 
						EVENT_TIME  =  1 << 1,
 | 
				
			||||||
 | 
						EVENT_DEFER =  1 << 2,
 | 
				
			||||||
 | 
						EVENT_ALL   = (1 << 3) - 1
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static intptr_t g_events = 0;
 | 
				
			||||||
 | 
					static intptr_t g_destroys = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					io_event_cb (pa_mainloop_api *a,
 | 
				
			||||||
 | 
						pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(void) a; (void) e; (void) fd; (void) events;
 | 
				
			||||||
 | 
						g_events |= (intptr_t) userdata;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					io_event_destroy_cb (pa_mainloop_api *a, pa_io_event *e, void *userdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(void) a; (void) e;
 | 
				
			||||||
 | 
						g_destroys += (intptr_t) userdata;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					time_event_cb (pa_mainloop_api *a,
 | 
				
			||||||
 | 
						pa_time_event *e, const struct timeval *tv, void *userdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(void) a; (void) e; (void) tv;
 | 
				
			||||||
 | 
						g_events |= (intptr_t) userdata;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					time_event_destroy_cb (pa_mainloop_api *a, pa_time_event *e, void *userdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(void) a; (void) e;
 | 
				
			||||||
 | 
						g_destroys += (intptr_t) userdata;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					defer_event_cb (pa_mainloop_api *a, pa_defer_event *e, void *userdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(void) a; (void) e;
 | 
				
			||||||
 | 
						g_events |= (intptr_t) userdata;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					defer_event_destroy_cb (pa_mainloop_api *a, pa_defer_event *e, void *userdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(void) a; (void) e;
 | 
				
			||||||
 | 
						g_destroys += (intptr_t) userdata;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					test_pulse (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct poller poller;
 | 
				
			||||||
 | 
						poller_init (&poller);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Let's just get this over with, not aiming for high test coverage here
 | 
				
			||||||
 | 
						pa_mainloop_api *api = poller_pa_new (&poller);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pa_io_event *ie = api->io_new (api, STDOUT_FILENO, PA_IO_EVENT_OUTPUT,
 | 
				
			||||||
 | 
							io_event_cb, (void *) EVENT_IO);
 | 
				
			||||||
 | 
						api->io_set_destroy (ie, io_event_destroy_cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const struct timeval tv = poller_pa_get_current_time ();
 | 
				
			||||||
 | 
						pa_time_event *te = api->time_new (api, &tv,
 | 
				
			||||||
 | 
							time_event_cb, (void *) EVENT_TIME);
 | 
				
			||||||
 | 
						api->time_set_destroy (te, time_event_destroy_cb);
 | 
				
			||||||
 | 
						api->time_restart (te, &tv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pa_defer_event *de = api->defer_new (api,
 | 
				
			||||||
 | 
							defer_event_cb, (void *) EVENT_DEFER);
 | 
				
			||||||
 | 
						api->defer_set_destroy (de, defer_event_destroy_cb);
 | 
				
			||||||
 | 
						api->defer_enable (api->defer_new (api,
 | 
				
			||||||
 | 
							defer_event_cb, (void *) EVENT_DEFER), false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						alarm (1);
 | 
				
			||||||
 | 
						while (g_events != EVENT_ALL)
 | 
				
			||||||
 | 
							poller_run (&poller);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						poller_pa_destroy (api);
 | 
				
			||||||
 | 
						soft_assert (g_destroys == EVENT_ALL);
 | 
				
			||||||
 | 
						poller_free (&poller);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Main --------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					main (int argc, char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct test test;
 | 
				
			||||||
 | 
						test_init (&test, argc, argv);
 | 
				
			||||||
 | 
						test_add_simple (&test, "/pulse", NULL, test_pulse);
 | 
				
			||||||
 | 
						return test_run (&test);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user