ap

Queue manager meant to be used as an audio player
git clone git://jacobedwards.org/ap
Log | Files | Refs | README | LICENSE

player.c (3103B)


      1 /*
      2  * Copyright 2021, 2022 Jacob R. Edwards
      3  *
      4  * ap -- audio player
      5  *
      6  * This program is free software: you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License as published by
      8  * the Free Software Foundation, either version 3 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include <sys/wait.h>
     21 
     22 #include <errno.h>
     23 #include <signal.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <unistd.h>
     28 
     29 #include "arg.h"
     30 #include "player.h"
     31 
     32 int
     33 pstart(struct player *p)
     34 {
     35 	char *prog;
     36 
     37 	if (p->state & (RUNNING | SUSPENDED)) {
     38 		errno = EALREADY;
     39 		return 1;
     40 	}
     41 
     42 	p->pid = fork();
     43 	if (p->pid < 0)
     44 		return 1;
     45 	if (p->pid > 0) {
     46 		p->state = RUNNING;
     47 		return 0;
     48 	}
     49 
     50 	prog = p->argv[0];
     51 	p->argv[0] = "apsplayer";
     52 	p->argv[p->argc] = p->path;
     53 
     54 	execvp(prog, p->argv);
     55 	perror(prog);
     56 	_exit(1);
     57 }
     58 
     59 int
     60 pstop(struct player *p)
     61 {
     62 	if (p->state == STOPPED) {
     63 		errno = EALREADY;
     64 		return 1;
     65 	}
     66 	if (p->state == SUSPENDED)
     67 		pcontinue(p);
     68 	if (p->state == RUNNING)
     69 		kill(p->pid, SIGTERM);	/* errors unconsequential */
     70 	p->state = STOPPED;
     71 	return 0;
     72 }
     73 
     74 int
     75 psuspend(struct player *p)
     76 {
     77 	if (p->state != RUNNING) {
     78 		errno = EINVAL;
     79 		return 1;
     80 	}
     81 
     82 	if (kill(p->pid, SIGSTOP) == -1)
     83 		return 1;
     84 	p->state = SUSPENDED;
     85 	return 0;
     86 }
     87 
     88 int
     89 pcontinue(struct player *p)
     90 {
     91 	if (p->state != SUSPENDED) {
     92 		errno = EINVAL;
     93 		return 1;
     94 	}
     95 
     96 	if (kill(p->pid, SIGCONT) == -1)
     97 		return 1;
     98 	p->state = RUNNING;
     99 	return 0;
    100 }
    101 
    102 int
    103 pplay(struct player *p, char *path)
    104 {
    105 	if (path == NULL && p->path == NULL) {
    106 		errno = ENOENT;
    107 		return 1;
    108 	}
    109 
    110 	if (path == NULL || (p->path != NULL && strcmp(p->path, path) == 0)) {
    111 		if (p->state == SUSPENDED)
    112 			return pcontinue(p);
    113 		return pstart(p);
    114 	}
    115 
    116 	if (p->state & (SUSPENDED | RUNNING) && pstop(p))
    117 		return 1;
    118 
    119 	free(p->path);
    120 	p->path = strdup(path);
    121 	if (p->path == NULL)
    122 		return 1;
    123 
    124 	return pstart(p);
    125 }
    126 
    127 int
    128 ptoggle(struct player *p)
    129 {
    130 	if (p->state == RUNNING)
    131 		return psuspend(p);
    132 	else
    133 		return pplay(p, NULL);
    134 }
    135 
    136 void
    137 pupdate(struct player *p, int status)
    138 {
    139 	if (WIFEXITED(status)) {
    140 		p->state = (WEXITSTATUS(status) ? FAILURE : COMPLETE);
    141 	} else if (WIFSIGNALED(status)) {
    142 		p->state = STOPPED;
    143 	} else if (WIFCONTINUED(status)) {
    144 		p->state = RUNNING;
    145 	} else if (WIFSTOPPED(status)) {
    146 		p->state = SUSPENDED;
    147 	}
    148 }
    149 
    150 void
    151 pfree(struct player *p)
    152 {
    153 	if (p == NULL)
    154 		return;
    155 	if (p->state != STOPPED)
    156 		pstop(p);
    157 	argfree(p->argv);
    158 	free(p->path);
    159 	free(p);
    160 }
    161 
    162 struct player *
    163 pnew(char **argv)
    164 {
    165 	struct player *p;
    166 
    167 	p = calloc(1, sizeof(*p));
    168 	if (p == NULL)
    169 		return p;
    170 
    171 	p->argc = arglen(argv);
    172 	p->argv = argdup(argv, p->argc + 1);
    173 	if (p->argv == NULL) {
    174 		pfree(p);
    175 		return NULL;
    176 	}
    177 	return p;
    178 }