When I call the function PeekEvents below the program prints zeros on the standard output and never finishes even though I type on the keyboard when the SDL window has focus. Why doesn't the function catch my keystrokes?
void PeekEvents(void)
{
SDL_Event events[1];
int count;
do {
count = SDL_PeepEvents(events, LEN(events), SDL_PEEKEVENT, SDL_EVENTMASK(SDL_KEYDOWN));
printf("%d\n", count);
} while (count == 0);
}
Here is the complete program:
#include <SDL/SDL.h>
#include <stdio.h>
#define LEN(a) ((int) (sizeof (a) / sizeof (a)[0]))
static void PeekEvents(void)
{
SDL_Event events[1];
int count;
do {
count = SDL_PeepEvents(events, LEN(events), SDL_PEEKEVENT, SDL_EVENTMASK(SDL_KEYDOWN));
printf("%d\n", count);
} while (count == 0);
}
static void Init(int *error)
{
SDL_Surface *display;
*error = SDL_Init(SDL_INIT_VIDEO);
if (! *error) {
display = SDL_SetVideoMode(640, 480, 8, 0);
if (display != NULL) {
*error = 0;
} else {
fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError());
*error = 1;
}
} else {
fprintf(stderr, "SDL_Init: %s\n", SDL_GetError());
*error = 1;
}
}
int main(void)
{
int error;
Init(&error);
if (! error) {
PeekEvents();
}
return error;
}
Documentation states somewhere that SDL_INIT_EVENTS is implicit when using SDL_INIT_VIDEO.
You need to add a call to SDL_PumpEvents in your loop, otherwise no messages will ever get in the queue.
SDL_PumpEvents gathers all the pending input information from devices and places it on the event queue. Without calls to SDL_PumpEvents no events would ever be placed on the queue. Often the need for calls to SDL_PumpEvents is hidden from the user since SDL_PollEvent and SDL_WaitEvent implicitly call SDL_PumpEvents. However, if you are not polling or waiting for events (e.g. you are filtering them), then you must call SDL_PumpEvents to force an event queue update.
Related
I have a program that receives messages via a socket and starts or stops playing a certain sound file depending on the message. In order for the "stop" message to work, I need the sound to play from a separate thread. My solution is to play the sound using alsa from a function I invoke using pthread_create(), and upon receiving a stop message I end the thread using pthread_cancel(). The function that plays the sound is called play_sound(void *args);
Here's what works:
struct args *args;
args->fp = fopen("path/to/soundfile.wav", "r");
args->volume = 1;
play_sound((void *) args);
but as soon as I try to run the function from within a new thread, I get no sound and 100% CPU usage on both my threads:
struct args *args;
int sound_thread;
args->fp = fopen("path/to/soundfile.wav", "r");
args->volume = 1;
pthread_create(&sound_thread, NULL, (void *) play_sound, (void *) args);
I have no idea where to even begin troubleshooting.
My code looks as follows:
#include <alsa/asoundlib.h>
#include <alsa/mixer.h>
#include <stdbool.h>
#include <pthread.h>
#include "server.h"
#include "sound.h"
//#include "log.h"
int sound_thread;
struct args {
FILE *fp;
float volume;
};
void init_sound ()
{
sound_thread = -1;
}
void stop_sound ()
{
if (sound_thread != -1) {
pthread_cancel(sound_thread);
keep_playing = false;
sound_thread = -1;
}
}
void dispatch_sound (FILE *fp, float volume)
{
// this function serves to create a new thread for the
// sound to be played from. This is what's giving me
// headaches.
if (sound_thread != -1) {
stop_sound();
}
struct args *args = (struct args *) malloc(sizeof(struct args));
args->fp = fp;
args->volume = volume;
if (pthread_create(&sound_thread, NULL, (void *) play_sound, args) != 0)
sound_thread = -1;
}
}
bool play_sound (void *args)
{
// This function actually plays the sound using functions
// from the alsa lib. it works when invoked regularly without
// threading.
keep_playing = true;
FILE *fp;
int volume;
bool success;
unsigned int samplerate;
int bufsz;
char *buf;
snd_pcm_t *pcm;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames;
samplerate = SAMPLERATE;
fp = ((struct args *) args)->fp;
volume = ((struct args *) args)->volume;
// volume is not actually used right now, since I took out
// the function that sets the volume before playing the
// audio in order to make it easier to pinpoint the issue.
if (snd_pcm_open(&pcm, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
success = false;
}
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(pcm, params);
if (snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_format(pcm, params, SND_PCM_FORMAT_S16_LE) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_channels(pcm, params, CHANNELS) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_rate_near(pcm, params, &samplerate, 0) < 0) {
success = false;
}´
if (snd_pcm_hw_params(pcm, params) < 0) {
success = false;
}
snd_pcm_hw_params_get_period_size(params, &frames, 0);
bufsz = frames * CHANNELS * SAMPLE_SIZE;
buf = (char *) malloc(bufsz);
while (keep_playing) {
while (fread(buf, bufsz, 1, fp) != 0 && keep_playing) {
int err;
if ((err = snd_pcm_writei(pcm, buf, frames)) == -EPIPE) {
snd_pcm_prepare(pcm);
}
}
rewind(fp);
}
snd_pcm_drain(pcm);
snd_pcm_close(pcm);
free(buf);
return success;
}
From the man page of pthread_cancel:
On Linux, cancellation is implemented using signals. Under the NPTL threading implementation, the first real-time signal (i.e., signal 32)
is used for this purpose. On LinuxThreads, the second real-time signal is used, if real-time signals are available, otherwise SIGUSR2 is
used.
In your while(keep_playing) loop, you aren't yielding the thread enough to handle the cancel signal; in your main thread; you aren't waiting for the result of the cancel request, ergo both threads hog the cpu.
A small delay before you restart playing the sound and pthread_join() after you call pthread_cancel should fix your problem.
I wanted to use libgps to interface with gpsd daemon. That's why I've implemented a little testing application in order to extract a value from a specific satellite.
The documentation on its HOWTO page tells us that
The tricky part is interpreting what you get from the blocking read.
The reason it’s tricky is that you’re not guaranteed that every read
will pick up exactly one complete JSON object from the daemon. It may
grab one response object, or more than one, or part of one, or one or
more followed by a fragment.
As recommended the documentation, the PACKET_SET mask bit is checked before doing anything else.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <gps.h>
#include <pthread.h>
pthread_t t_thread;
struct t_args {
unsigned int ID;
};
unsigned int status = 0;
int elevation;
int p_nmea(void *targs);
void start_test(void)
{
struct t_args *args = malloc(sizeof *args);
status = 1;
args->ID = 10;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (pthread_create(&t_thread, &attr, (void *)&p_nmea, args) != 0)
{
perror("create: \n");
}
}
int test_result(int * Svalue)
{
int res;
if(status == 1)
{
void * t_res;
if(pthread_tryjoin_np(t_thread, &t_res) != 0)
{
status = 1;
}
else
{
if((int)t_res == 1)
{
res = 3;
*Svalue = elevation;
elevation = 0;
}
else
{
res = 4;
}
}
}
return res;
}
int p_nmea(void *targs)
{
struct t_args *thread_args = targs;
struct gps_data_t gpsdata;
int ret = -1;
int count = 10;
int i,j;
if(gps_open((char *)"localhost", (char *)DEFAULT_GPSD_PORT, &gpsdata) != 0)
{
(void)fprintf(stderr, "cgps: no gpsd running or network error: %d, %s\n", errno, gps_errstr(errno));
return (-1);
}
else
{
(void)gps_stream(&gpsdata, WATCH_ENABLE, NULL);
do
{
if(!gps_waiting(&gpsdata, 1000000))
{
(void)gps_close(&gpsdata);
}
else
{
if(gps_read(&gpsdata) == -1)
{
return (-1);
}
else
{
if(gpsdata.set & PACKET_SET)
{
for (i = 0; i < MAXCHANNELS; i++)
{
for (j = 0; j < gpsdata->satellites_visible; j++)
{
if(gpsdata->PRN[i] == thread_args.ID)
{
elevation = (int)gpsdata->elevation[i];
ret = 1;
break;
}
}
if(gpsdata->PRN[i] == thread_args.ID)
{
break;
}
}
}
}
}
--count;
}while(count != 0);
}
(void)gps_stream(&gpsdata, WATCH_DISABLE, NULL);
(void)gps_close(&gpsdata);
(void)free(thread_args);
(void)pthread_exit((void*) ret);
}
As recommended in the documentation too, I had a look at cgps and gpxlogger for example codes, but the subtleties of libgps escape me. A while loop has been added before gps_waiting() in order to get, at least, one entire response object. Before introducing pthread, I noted that call the function test_result() just after start_test() take few seconds before returning an answer. By using a thread I thought that 3 would be imediately returned, then 3 or 4 .. but it's not ! I am still losing few seconds. In addition, I voluntarily use pthread_tryjoin_np() because its man page says
The pthread_tryjoin_np() function performs a nonblocking join with the thread
Can anybody give me his help, I guess that I understand something wrongly but I am not able to say about which part yet? Basically, why I come into the do while loop at least four times before returning the first value ?
EDIT 1 :
After reading the documentation HOWTO again I highlight the lines :
The fact that the data-waiting check and the read both block means that, if your application has to deal with other input sources than the GPS, you will probably have to isolate the read loop in a thread with a mutex lock on the gps_data structure.
I am a little bit confusing. What does it really mean ?
Your loop is executing multiple times before returning a full packet because you do not have a sleep condition. Therefore each time the daemon registers a packet (even when not a full NMEA message), the gps_waiting() function returns. I'd recommend sleeping at least as long as it takes your GPS to register a full message.
For example, if you expect GPPAT messages, you could reasonably expect to have 12 characters in the message. Thus at 9600 baud, that would take 1/17.5 seconds or about 57 ms. In this case, your code could look like this:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <gps.h>
#include <pthread.h>
pthread_t t_thread;
struct t_args {
unsigned int ID;
};
unsigned int status = 0;
int elevation;
int p_nmea(void *targs);
void start_test(void)
{
struct t_args *args = malloc(sizeof *args);
status = 1;
args->ID = 10;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (pthread_create(&t_thread, &attr, (void *)&p_nmea, args) != 0)
{
perror("create: \n");
}
}
int test_result(int * Svalue)
{
int res;
if(status == 1)
{
void * t_res;
if(pthread_tryjoin_np(t_thread, &t_res) != 0)
{
status = 1;
}
else
{
if((int)t_res == 1)
{
res = 3;
*Svalue = elevation;
elevation = 0;
}
else
{
res = 4;
}
}
}
return res;
}
int p_nmea(void *targs)
{
struct t_args *thread_args = targs;
struct gps_data_t gpsdata;
int ret = 0;
int count = 10;
int i,j;
if(gps_open((char *)"localhost", (char *)DEFAULT_GPSD_PORT, &gpsdata) != 0)
{
(void)fprintf(stderr, "cgps: no gpsd running or network error: %d, %s\n", errno, gps_errstr(errno));
return (-1);
}
else
{
(void)gps_stream(&gpsdata, WATCH_ENABLE, NULL);
do
{
ret = 0; // Set this here to allow breaking correctly
usleep(50000); // Sleep here to wait for approx 1 msg
if(!gps_waiting(&gpsdata, 1000000)) break;
if(gps_read(&gpsdata) == -1) break;
if(gpsdata.set & PACKET_SET)
{
for (i = 0; i < MAXCHANNELS && !ret; i++)
{
for (j = 0; j < gpsdata.satellites_visible; j++)
{
if(gpsdata.PRN[i] == thread_args.ID)
{
elevation = (int)gpsdata.elevation[i]; // Be sure to not deref structure here
ret = 1;
break;
}
}
}
--count;
}while(count != 0);
}
(void)gps_stream(&gpsdata, WATCH_DISABLE, NULL);
(void)gps_close(&gpsdata);
(void)free(thread_args);
(void)pthread_exit((void*) ret);
}
Alternatively, you could just set your count higher and wait for the full message.
Here is a simple program using libevent on linux, it tracks the stdout fd, when it's writable, the callback will print some info to stdout.
Code
hello_libevent.c:
// libevent hello
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <event2/event.h>
#include <event2/thread.h>
void event_callback(evutil_socket_t fd, short events, void *arg) {
if(events | EV_WRITE) {
write(fd, "hello\n", 7);
}
sleep(1);
}
int libevent_test() {
int opr;
// enable pthread
if(evthread_use_pthreads() == -1) {
printf("error while evthread_use_pthreads(): %s\n", strerror(errno));
return -1;
}
// create event_base
struct event_base* eb;
if((eb = event_base_new()) == NULL) {
printf("error while event_base_new(): %s\n", strerror(errno));
return -1;
}
// create event
int fd_stdout = fileno(stdout);
struct event* event_stdout;
event_stdout = event_new(eb, fd_stdout, EV_WRITE, &event_callback, NULL);
// add event as pending
struct timeval timeout = {10, 0};
if(event_add(event_stdout, &timeout) == -1) {
printf("error while event_add(): %s\n", strerror(errno));
return -1;
}
// dispatch
if((opr = event_base_loop(eb, EVLOOP_NONBLOCK)) == -1) {
printf("error while event_base_dispatch(): %s\n", strerror(errno));
return -1;
} else if(opr == 1) {
printf("no more events\n");
} else {
printf("exit normally\n");
}
// free event
event_free(event_stdout);
return 0;
}
int main(int argc, char * argv[]) {
return libevent_test();
}
Compile:
gcc -Wall hello_libevent.c -levent -levent_pthreads
Execution result:
hello
no more events
Questions:
In the test, event only occur once, is that the expected behavior? Or it should loop to get more event until timeout?
How to make it get event continuously? Is it necessary to call event_base_loop within a loop, while it's already a loop?
I think the event flag EV_PERSIST mentioned in event.h File Reference might help.
Persistent event: won't get removed automatically when activated.
When a persistent event with a timeout becomes activated, its timeout is reset to 0.
Instead of
//...
event_stdout = event_new(eb, fd_stdout, EV_WRITE, &event_callback, NULL);
//...
you can pass this flag to event_new
//...
event_stdout = event_new(eb, fd_stdout, EV_WRITE|EV_PERSIST, &event_callback, NULL);
//...
and the other parts of code remain unchanged. At this time you create and add an event once and there's no need to call event_base_loop within a loop.
The compiled program just keeps printing lines of "hello" until it is terminated.
By the way, I noticed that changing
write(fd, "hello\n", 7);
into
write(fd, "hello\n", 6);
eliminates the leading character '\0' of each line.
From http://www.wangafu.net/~nickm/libevent-book/Ref3_eventloop.html it looks like you can call event_base_loop or event_base_dispatch from within a loop.
while (1) {
/* This schedules an exit ten seconds from now. */
event_base_loopexit(base, &ten_sec);``
event_base_dispatch(base);
puts("Tick");
}
The main purpose of events is to inform some busy thread about some event that happened elsewhere. So, this looks logical.
How do you listen to changes in volume on the Master channel on the default sound card? I'd like to be notified through dbus or a callback or something that the volume has changed.
I have tried looking and the ALSA and PulseAudio APIs and they only seem to allow you to set and get the volume, but not listen for changes in the volume.
Any programming language is fine.
Edit: In the second example, an event isn't generated for me when volume is below 5% or above 100%. The first example works perfectly as far as I know.
pactl subscribe will print out data about the sinks when the volume changes. What I'm doing now is piping the output to a small C program that will run a script.
run.sh:
pactl subscribe | grep --line-buffered "sink" | ./prog
or for a specific sink, e.g. 3:
pactl subscribe | grep --line-buffered "sink #3" | ./prog
prog.c:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
while(1){
while(getchar() != '\n');
system("./volume_notify.sh");
}
}
When the volume of the sink is changed, pactl will print a line, which will cause the program to run the script.
-or-
Here's an example based on the amixer monitor, as referenced by CL.
The while loop will iterate each time the volume changes, so put your callback in there.
#include <stdio.h>
#include <alsa/asoundlib.h>
#define MAX_CARDS 256
int monitor_native(char const *name);
int open_ctl(const char *name, snd_ctl_t **ctlp);
void close_all(snd_ctl_t* ctls[], int ncards);
int main(int argc, char* argv[]){
const char *ctl_name = "hw:0";
while(monitor_native(ctl_name) == 1){
//volume has been changed, do something
system("~/.volume_notify.sh");
}
return 0;
}
int monitor_native(char const *name) {
snd_ctl_t *ctls[MAX_CARDS];
int ncards = 0;
int i, err = 0;
if (!name) {
int card = -1;
while (snd_card_next(&card) >= 0 && card >= 0) {
char cardname[16];
if (ncards >= MAX_CARDS) {
fprintf(stderr, "alsactl: too many cards\n");
close_all(ctls, ncards);
return -E2BIG;
}
sprintf(cardname, "hw:%d", card);
err = open_ctl(cardname, &ctls[ncards]);
if (err < 0) {
close_all(ctls, ncards);
return err;
}
ncards++;
}
} else {
err = open_ctl(name, &ctls[0]);
if (err < 0) {
close_all(ctls, ncards);
return err;
}
ncards++;
}
for (;ncards > 0;) {
pollfd* fds = new pollfd[ncards];
for (i = 0; i < ncards; i++) {
snd_ctl_poll_descriptors(ctls[i], &fds[i], 1);
}
err = poll(fds, ncards, -1);
if (err <= 0) {
err = 0;
break;
}
for (i = 0; i < ncards; i++) {
unsigned short revents;
snd_ctl_poll_descriptors_revents(ctls[i], &fds[i], 1, &revents);
if (revents & POLLIN) {
snd_ctl_event_t *event;
snd_ctl_event_alloca(&event);
if (snd_ctl_read(ctls[i], event) < 0) {
continue;
}
if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM) {
continue;
}
unsigned int mask = snd_ctl_event_elem_get_mask(event);
if (mask & SND_CTL_EVENT_MASK_VALUE) {
close_all(ctls, ncards);
return 1;
}
}
}
}
close_all(ctls, ncards);
return 0;
}
int open_ctl(const char *name, snd_ctl_t **ctlp) {
snd_ctl_t *ctl;
int err;
err = snd_ctl_open(&ctl, name, SND_CTL_READONLY);
if (err < 0) {
fprintf(stderr, "Cannot open ctl %s\n", name);
return err;
}
err = snd_ctl_subscribe_events(ctl, 1);
if (err < 0) {
fprintf(stderr, "Cannot open subscribe events to ctl %s\n", name);
snd_ctl_close(ctl);
return err;
}
*ctlp = ctl;
return 0;
}
void close_all(snd_ctl_t* ctls[], int ncards) {
for (ncards -= 1; ncards >= 0; --ncards) {
snd_ctl_close(ctls[ncards]);
}
}
This is possible with the ALSA API.
When you have a control device, call snd_ctl_subscribe_events() to enable events.
Then use snd_ctl_read() to read events; to wait for them, use blocking mode or poll().
If the event is of type SND_CTL_EVENT_ELEM, and if its event bit mask contains SND_CTL_EVENT_MASK_VALUE, that element's value has changed.
See the implementation of amixer monitor for an example.
Similar to #sealj553's answer, but does not need a C-program:
pactl subscribe | grep --line-buffered "sink" | xargs -n1 ./volume_notify.sh
In my SDL 1.2 program I have a function KeyPressed which needs to check if a key has been pressed. If the mouse pointer has not been moved in front of the window the program halts as intended when a key is pressed. On the other hand, if the mouse pointer is moved in front of the window the event queue seems to fill up and after that the program does not respond to key presses. Is there a way to empty the event queue when there are no keyboard events in it?
#include <SDL/SDL.h>
#include <stdio.h>
static void Init(int *error)
{
SDL_Surface *display;
*error = SDL_Init(SDL_INIT_VIDEO);
if (! *error) {
display = SDL_SetVideoMode(640, 480, 8, 0);
if (display != NULL) {
*error = 0;
} else {
fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError());
*error = 1;
}
} else {
fprintf(stderr, "SDL_Init: %s\n", SDL_GetError());
*error = 1;
}
}
static int KeyPressed(void)
{
SDL_Event event;
int count, result;
result = 0;
SDL_PumpEvents();
count = SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_EVENTMASK(SDL_KEYDOWN));
switch (count) {
case -1:
fprintf(stderr, "SDL_PeepEvents: %s\n", SDL_GetError());
break;
case 0:
break;
case 1:
result = 1;
break;
}
return result;
}
int main(void)
{
int error;
Init(&error);
if (! error) {
do {
/*nothing*/
} while (! KeyPressed());
}
return error;
}
You should be checking event's in a while loop using polling which will pop the events from the queue as they are handled. Use either multiple if/then statements or a swicth block for handling the event. I recommend this method as you can easily miss important events such as SDL_QUIT when using SDL_PeepEvent
Basic example:
SDL_Event e;
while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT)
{
// do something here
}
}
The following solution, inspired by Zammalad's comment seems to work. When no key down event is found we remove one event (typically a mouse event) from the queue to make room for new events. To make the code more robust I have also replaced the scalar event with the array events.
#define LEN(a) ((int) (sizeof (a) / sizeof (a)[0]))
static int KeyPressed(void)
{
SDL_Event events[1];
int count, result;
result = 0;
SDL_PumpEvents();
/*search the event queue for key down events*/
count = SDL_PeepEvents(events, LEN(events), SDL_PEEKEVENT, SDL_EVENTMASK(SDL_KEYDOWN));
switch (count) {
case -1:
fprintf(stderr, "SDL_PeepEvents: %s\n", SDL_GetError());
break;
case 0:
/*make room for new events by removing (non-key down) event from the queue*/
count = SDL_PeepEvents(events, LEN(events), SDL_GETEVENT, -1);
if ((count > 0) && (events[0].type == SDL_QUIT)) {
exit(EXIT_SUCCESS);
}
break;
case 1:
result = 1;
break;
}
return result;
}