summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--volume.cpp145
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;
+}