music_cmd.c
author Ozkan Sezer <sezeroz@gmail.com>
Sun, 07 Oct 2018 00:08:00 +0300
branchSDL-1.2
changeset 871 284d10d87cc9
parent 573 23d69b6621d0
child 601 05123263dab3
child 892 9dd046cb6cd7
permissions -rw-r--r--
backport fix for bug #1833. (from 2.0 branch commit 7ead8213dfb0).
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 /* This file supports an external command for playing music */
    24 
    25 #ifdef CMD_MUSIC
    26 
    27 #include <sys/types.h>
    28 #include <sys/wait.h>
    29 #include <stdio.h>
    30 #include <stdlib.h>
    31 #include <unistd.h>
    32 #include <string.h>
    33 #include <signal.h>
    34 #include <ctype.h>
    35 
    36 #include "SDL_mixer.h"
    37 #include "music_cmd.h"
    38 
    39 /* Unimplemented */
    40 void MusicCMD_SetVolume(int volume)
    41 {
    42 	Mix_SetError("No way to modify external player volume");
    43 }
    44 
    45 /* Load a music stream from the given file */
    46 MusicCMD *MusicCMD_LoadSong(const char *cmd, const char *file)
    47 {
    48 	MusicCMD *music;
    49 
    50 	/* Allocate and fill the music structure */
    51 	music = (MusicCMD *)SDL_malloc(sizeof *music);
    52 	if ( music == NULL ) {
    53 		Mix_SetError("Out of memory");
    54 		return(NULL);
    55 	}
    56 	music->file = SDL_strdup(file);
    57 	music->cmd = SDL_strdup(cmd);
    58 	music->pid = 0;
    59 
    60 	/* We're done */
    61 	return(music);
    62 }
    63 
    64 /* Parse a command line buffer into arguments */
    65 static int ParseCommandLine(char *cmdline, char **argv)
    66 {
    67 	char *bufp;
    68 	int argc;
    69 
    70 	argc = 0;
    71 	for ( bufp = cmdline; *bufp; ) {
    72 		/* Skip leading whitespace */
    73 		while ( isspace(*bufp) ) {
    74 			++bufp;
    75 		}
    76 		/* Skip over argument */
    77 		if ( *bufp == '"' ) {
    78 			++bufp;
    79 			if ( *bufp ) {
    80 				if ( argv ) {
    81 					argv[argc] = bufp;
    82 				}
    83 				++argc;
    84 			}
    85 			/* Skip over word */
    86 			while ( *bufp && (*bufp != '"') ) {
    87 				++bufp;
    88 			}
    89 		} else {
    90 			if ( *bufp ) {
    91 				if ( argv ) {
    92 					argv[argc] = bufp;
    93 				}
    94 				++argc;
    95 			}
    96 			/* Skip over word */
    97 			while ( *bufp && ! isspace(*bufp) ) {
    98 				++bufp;
    99 			}
   100 		}
   101 		if ( *bufp ) {
   102 			if ( argv ) {
   103 				*bufp = '\0';
   104 			}
   105 			++bufp;
   106 		}
   107 	}
   108 	if ( argv ) {
   109 		argv[argc] = NULL;
   110 	}
   111 	return(argc);
   112 }
   113 
   114 static char **parse_args(char *command, char *last_arg)
   115 {
   116 	int argc;
   117 	char **argv;
   118 
   119 	/* Parse the command line */
   120 	argc = ParseCommandLine(command, NULL);
   121 	if ( last_arg ) {
   122 		++argc;
   123 	}
   124 	argv = (char **)SDL_malloc((argc+1)*(sizeof *argv));
   125 	if ( argv == NULL ) {
   126 		return(NULL);
   127 	}
   128 	argc = ParseCommandLine(command, argv);
   129 
   130 	/* Add last command line argument */
   131 	if ( last_arg ) {
   132 		argv[argc++] = last_arg;
   133 	}
   134 	argv[argc] = NULL;
   135 
   136 	/* We're ready! */
   137 	return(argv);
   138 }
   139 
   140 /* Start playback of a given music stream */
   141 void MusicCMD_Start(MusicCMD *music)
   142 {
   143 #ifdef HAVE_FORK
   144 	music->pid = fork();
   145 #else
   146 	music->pid = vfork();
   147 #endif
   148 	switch(music->pid) {
   149 	    /* Failed fork() system call */
   150 	    case -1:
   151 		Mix_SetError("fork() failed");
   152 		return;
   153 
   154 	    /* Child process - executes here */
   155 	    case 0: {
   156 		    char *command;
   157 		    char **argv;
   158 
   159 		    /* Unblock signals in case we're called from a thread */
   160 		    {
   161 			sigset_t mask;
   162 			sigemptyset(&mask);
   163 			sigprocmask(SIG_SETMASK, &mask, NULL);
   164 		    }
   165 
   166 		    /* Execute the command */
   167 		    command = SDL_strdup(music->cmd);
   168 		    argv = parse_args(command, music->file);
   169 		    if ( argv != NULL ) {
   170 			execvp(argv[0], argv);
   171 		    }
   172 		    SDL_free(command);
   173 
   174 		    /* exec() failed */
   175 		    perror(argv[0]);
   176 		    _exit(-1);
   177 		}
   178 		break;
   179 
   180 	    /* Parent process - executes here */
   181 	    default:
   182 		break;
   183 	}
   184 	return;
   185 }
   186 
   187 /* Stop playback of a stream previously started with MusicCMD_Start() */
   188 void MusicCMD_Stop(MusicCMD *music)
   189 {
   190 	int status;
   191 
   192 	if ( music->pid > 0 ) {
   193 		while ( kill(music->pid, 0) == 0 ) {
   194 			kill(music->pid, SIGTERM);
   195 			sleep(1);
   196 			waitpid(music->pid, &status, WNOHANG);
   197 		}
   198 		music->pid = 0;
   199 	}
   200 }
   201 
   202 /* Pause playback of a given music stream */
   203 void MusicCMD_Pause(MusicCMD *music)
   204 {
   205 	if ( music->pid > 0 ) {
   206 		kill(music->pid, SIGSTOP);
   207 	}
   208 }
   209 
   210 /* Resume playback of a given music stream */
   211 void MusicCMD_Resume(MusicCMD *music)
   212 {
   213 	if ( music->pid > 0 ) {
   214 		kill(music->pid, SIGCONT);
   215 	}
   216 }
   217 
   218 /* Close the given music stream */
   219 void MusicCMD_FreeSong(MusicCMD *music)
   220 {
   221 	SDL_free(music->file);
   222 	SDL_free(music->cmd);
   223 	SDL_free(music);
   224 }
   225 
   226 /* Return non-zero if a stream is currently playing */
   227 int MusicCMD_Active(MusicCMD *music)
   228 {
   229 	int status;
   230 	int active;
   231 
   232 	active = 0;
   233 	if ( music->pid > 0 ) {
   234 		waitpid(music->pid, &status, WNOHANG);
   235 		if ( kill(music->pid, 0) == 0 ) {
   236 			active = 1;
   237 		}
   238 	}
   239 	return(active);
   240 }
   241 
   242 #endif /* CMD_MUSIC */