diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | volume.cpp | 145 |
2 files changed, 147 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..917f4b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.swp +volume diff --git a/volume.cpp b/volume.cpp new file mode 100644 index 0000000..21f625b --- /dev/null +++ b/volume.cpp @@ -0,0 +1,145 @@ +#include <iostream> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <unistd.h> +#include <termios.h> + +using namespace std; + +int printusage(int,const char *const *argv,const char *message=NULL){ + if(message)cout<<message<<endl; + cout<<"Usage: "<<argv[0]<<" [volume]"<<endl; + cout<<"If [volume] is given, changes volume to that value. (Integer in [0,100].)"<<endl; + cout<<"Otherwise, it shows a graphical slider, adjustable with the arrow keys or h/l. If the"<<endl; + cout<<"output is not a tty, the graphical slider is disabled."<<endl; + return 1; +} + +int setvolume(int vol){ + char *cmd=NULL; + asprintf(&cmd,"osascript -e 'set volume output volume %d'",vol); + if(!cmd)return 1; + int ret=system(cmd); + free(cmd); + return ret; +} + +int getvolume(void){ + FILE *p=popen("osascript -e 'output volume of (get volume settings)'","r"); + if(!p)return 50; + int vol; + fscanf(p,"%d",&vol); + pclose(p); + return vol; +} + +int setmuted(bool m){ + char *cmd=NULL; + asprintf(&cmd,"osascript -e 'set volume output muted %s'",m?"true":"false"); + if(!cmd)return 1; + int ret=system(cmd); + free(cmd); + return ret; +} + +bool getmuted(void){ + FILE *p=popen("osascript -e 'output muted of (get volume settings)'","r"); + if(!p)return false; + char s[16]; + fscanf(p,"%15s",s); + pclose(p); + if(strcmp(s,"true")==0)return true; + else if(strcmp(s,"false")==0)return false; + else return false; +} + +void displayslider(bool m,int vol){ + cout<<"\x1B[2K\r0 |" + <<string(vol/5,'#')<<string((100-vol)/5,'-') + <<"| 100 ("<<vol<<"%)"; + if(m)cout<<" \x1B[1mMUTED (m)\x1B[0m"; + cout.flush(); +} + +int incvol(int &vol,int inc){ + if((vol==0&&inc<=0)||(vol==100&&inc>=0))return 0; + vol+=inc; + if(vol<0)vol=0; + if(vol>100)vol=100; + return setvolume(vol); +} + +int interact(bool &m,int &vol){ + char c=cin.get(); + if(c=='q'||c=='Q'||c=='\n'||c=='\r')return 1; + if(c=='l')return incvol(vol,5); + if(c=='h')return incvol(vol,-5); + if(c=='m'){ + m=!m; + setmuted(m); + return 0; + } + if(c!=27){ + cout<<7<<flush; + return 0; + } + c=cin.get(); + if(c!='['){ + cout<<7<<flush; + return 0; + } + c=cin.get(); + if(c=='C')return incvol(vol,5); + if(c=='D')return incvol(vol,-5); + cout<<7<<flush; + return 0; +} + +struct termios tios_bak; + +void initscreen(void){ + struct termios tios; + tcgetattr(0,&tios_bak); + tios=tios_bak; + tios.c_lflag&=~( + ECHO|ECHOE|ECHOKE //no echo of normal characters, erasing and killing + |ECHOCTL //don't visibly echo control characters (^V etc.) + |ECHONL //don't even echo a newline + |ICANON //disable canonical mode +#ifdef NOKERNINFO + |NOKERNINFO //don't print a status line on ^T +#endif + |IEXTEN //don't handle things like ^V specially + //|ISIG //disable ^C ^\ and ^Z + ); + tios.c_cc[VMIN]=1; //read one char at a time + tios.c_cc[VTIME]=0; //no timeout on reading, make it a blocking read + tcsetattr(0,TCSAFLUSH,&tios); +} + +void endscreen(void){ + tcsetattr(0,TCSAFLUSH,&tios_bak); +} + +int main(int argc,char **argv){ + if(argc>2)return printusage(argc,argv); + if(argc==2){ + if(argv[1][0]=='\0')return printusage(argc,argv,"Invalid volume format"); + char *endp; + const int vol=strtol(argv[1],&endp,10); + if(*endp!='\0')return printusage(argc,argv,"Invalid volume format"); + if(vol<0||vol>100)return printusage(argc,argv,"Volume argument not in range"); + return setvolume(vol); + } + if(!isatty(1))return printusage(argc,argv,"Won't display slider if stdout is not a terminal"); + initscreen(); + atexit(endscreen); + bool m=getmuted(); + int vol=getvolume(); + while(true){ + displayslider(m,vol); + if(interact(m,vol))break; + } + cout<<endl; +} |