1 /* aylet 0.2, a .AY music file player.
2  * Copyright (C) 2001 Russell Marks and Ian Collier. See main.c for licence.
3  *
4  * ui.c - curses UI code (and no-UI UI :-)).
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <sys/types.h>
12 #include <signal.h>
13 #include <curses.h>
14 #include "main.h"
15 
16 #include "ui.h"
17 
18 
19 static int frame_x=1,frame_y=5;	/* frame_y is adjusted */
20 /* XXX should adjust frame_x when >80 cols, too... */
21 
22 static int used_curses_ui=0,need_update=1,need_nexttrack=0;
23 
24 
clearscreen(void)25 static void clearscreen(void)
26 {
27 wclear(stdscr);
28 move(0,0); refresh();
29 }
30 
31 
draw_frame(void)32 static void draw_frame(void)
33 {
34 int y=frame_y;
35 
36 move(frame_y,frame_x);
37 addstr(",--------------------------------------");
38 addstr("--------------------------------------.");
39 move(frame_y+12,frame_x);
40 addstr("`--------------------------------------");
41 addstr("--------------------------------------'");
42 move(frame_y+6,frame_x+1);
43 addstr("--------------------------------------");
44 addstr("--------------------------------------");
45 move(frame_y+9,frame_x+1);
46 addstr("--------------------------------------");
47 addstr("--------------------------------------");
48 
49 for(y=frame_y+1;y<frame_y+12;y++)
50   {
51   mvaddch(y,frame_x,'|');
52   mvaddch(y,frame_x+77,'|');
53   }
54 
55 mvaddstr(frame_y+1,frame_x+2,"   File:");
56 mvaddstr(frame_y+2,frame_x+2,"   Misc:");
57 mvaddstr(frame_y+3,frame_x+2," Author:");
58 mvaddstr(frame_y+4,frame_x+2," Tracks:");
59 mvaddstr(frame_y+5,frame_x+2,"Playing:");
60 mvaddstr(frame_y+7,frame_x+2," Status:");
61 mvaddstr(frame_y+8,frame_x+2,"   Time:");
62 for(y=frame_y+7;y<=frame_y+8;y++)
63   {
64   mvaddch(y,frame_x+24,'|');
65   mvaddch(y,frame_x+52,'|');
66   }
67 mvaddstr(frame_y+7,frame_x+30,"[H]igh-speed:");
68 mvaddstr(frame_y+7,frame_x+55,"[S]top after:");
69 mvaddstr(frame_y+8,frame_x+55,"[F]ade time:");
70 
71 mvaddstr(frame_y+10,frame_x+4,
72          "[ z - prev | x - play | c - pause | v - stop | b - next |"
73          " r - restart ]");
74 mvaddstr(frame_y+11,frame_x+12,
75          "[ space - next file | del - previous file | q - quit ]");
76 
77 move(0,0);
78 refresh();
79 }
80 
81 
fitwidth(char * str,int decr)82 static char *fitwidth(char *str,int decr)
83 {
84 static char buf[65+1];	/* width allowed, plus NUL */
85 int len=strlen(str);
86 int maxlen=sizeof(buf)-1-decr;
87 
88 memset(buf,' ',maxlen);
89 buf[maxlen]=0;
90 
91 if(str && sizeof(buf)-decr>=1)
92   {
93   if(len>maxlen) len=maxlen;
94   memcpy(buf,str,len);
95   }
96 
97 return(buf);
98 }
99 
100 
draw_status(char * filename,char * misc,char * author,int track,char * playingstr)101 static void draw_status(char *filename,char *misc,
102                         char *author,int track,char *playingstr)
103 {
104 char *ptr=strrchr(filename,'/');
105 
106 mvaddstr(frame_y+1,frame_x+11,fitwidth(ptr?ptr+1:filename,0));
107 mvaddstr(frame_y+2,frame_x+11,fitwidth(misc,0));
108 mvaddstr(frame_y+3,frame_x+11,fitwidth(author,0));
109 mvprintw(frame_y+4,frame_x+11,"%3d",aydata.num_tracks);
110 mvprintw(frame_y+5,frame_x+11,"%3d - %s",track,fitwidth(playingstr,6));
111 
112 mvaddstr(frame_y+7,frame_x+11,paused?"paused ":(playing?"playing":"stopped"));
113 mvaddstr(frame_y+7,frame_x+44,highspeed?"on ":"off");
114 
115 move(frame_y+7,frame_x+69);
116 if(!stopafter)
117   addstr("--:--");
118 else
119   printw("%2d:%02d",stopafter/60,stopafter%60);
120 
121 mvprintw(frame_y+8,frame_x+68,"%2d sec",fadetime);
122 
123 move(0,0);
124 refresh();
125 }
126 
127 
draw_status_timeonly(void)128 static void draw_status_timeonly(void)
129 {
130 mvprintw(frame_y+8,frame_x+11,"%2d:%02d  ",tunetime.min,tunetime.sec);
131 
132 move(0,0);
133 refresh();
134 }
135 
136 
non_ui_frame(void)137 static int non_ui_frame(void)
138 {
139 static int oldfile=-1;
140 static int oldtrack=-1;
141 
142 if(need_update)
143   {
144   need_update=0;
145   if(ay_file!=oldfile)
146     {
147     char *filename=ay_filenames[ay_file];
148     char *ptr=strrchr(filename,'/');
149 
150     fprintf(stderr,
151             "\n   File: %s\n   Misc: %s\n Author: %s\n Tracks: %3d\n",
152             ptr?ptr+1:filename,aydata.miscstr,aydata.authorstr,
153             aydata.num_tracks);
154     }
155   if(ay_file!=oldfile || ay_track!=oldtrack)
156     {
157     oldtrack=ay_track;
158     fprintf(stderr,
159             "Playing: %3d - %s\n",
160             ay_track+1,aydata.tracks[ay_track].namestr);
161     }
162   oldfile=ay_file;
163   }
164 
165 /* quit if we stop (i.e. when we've played all tracks) */
166 if(!playing && !paused)
167   return(action_callback(cb_quit));
168 
169 if(need_nexttrack)
170   {
171   need_nexttrack=0;
172   return(action_callback(cb_next_track));
173   }
174 
175 return(1);
176 }
177 
178 
179 /* called per 1/50th, this deals with the usual events.
180  * returns zero if we want to stop the current track.
181  */
ui_frame(void)182 int ui_frame(void)
183 {
184 enum cb_action_tag action;
185 
186 if(!use_ui)
187   return(non_ui_frame());
188 
189 if(need_update)
190   {
191   need_update=0;
192   draw_frame();
193   draw_status(ay_filenames[ay_file],(char *)aydata.miscstr, (char *)aydata.authorstr,
194               ay_track+1, (char *)aydata.tracks[ay_track].namestr);
195   }
196 
197 /* update time display */
198 draw_status_timeonly();
199 
200 /* read keys */
201 action=cb_none;
202 switch(getch())
203   {
204   case 27: case 'q':	action=cb_quit; break;
205   case 'h':		action=cb_highspeed; break;
206 #if KEY_BACKSPACE!=8
207   case 8:
208 #endif
209 #if KEY_DC!=127
210   case 127:
211 #endif
212   case KEY_BACKSPACE: case KEY_DC:
213     action=cb_prev_file; break;
214   case ' ':		action=cb_next_file; break;
215   case 'z':		action=cb_prev_track; break;
216   case 'b':		action=cb_next_track; break;
217   case 'x':		action=cb_play; break;
218   case 'c':		action=cb_pause; break;
219   case 'v':		action=cb_stop; break;
220   case 'r':		action=cb_restart; break;
221   case 'S':		action=cb_dec_stopafter; break;
222   case 's':		action=cb_inc_stopafter; break;
223   case 'F':		action=cb_dec_fadetime; break;
224   case 'f':		action=cb_inc_fadetime; break;
225   }
226 
227 if(action==cb_none)
228   return(1);
229 
230 return(action_callback(action));
231 }
232 
233 
234 /* called if playback status has changed without us knowing. */
ui_change_notify(void)235 void ui_change_notify(void)
236 {
237 need_update=1;
238 }
239 
240 
non_ui_ctrlc(int foo)241 static void non_ui_ctrlc(int foo)
242 {
243 if(tunetime.min==0 && tunetime.sec==0 &&
244    tunetime.subsecframes<25)
245   playing=0;	/* quit */
246 else
247   need_nexttrack=1;
248 }
249 
250 
non_ui_init(void)251 static void non_ui_init(void)
252 {
253 struct sigaction sa;
254 
255 sa.sa_flags=SA_RESTART;
256 sa.sa_handler=non_ui_ctrlc;
257 sigaction(SIGINT,&sa,NULL);
258 
259 need_update=1;
260 }
261 
262 
ui_init(int argc,char ** argv)263 void ui_init(int argc,char **argv)
264 {
265 if(!use_ui)
266   {
267   non_ui_init();
268   return;
269   }
270 
271 used_curses_ui=1;
272 
273 initscr();
274 cbreak(); noecho();
275 curs_set(0);	/* remove cursor (if possible) */
276 keypad(stdscr,TRUE);
277 nodelay(stdscr,1);
278 
279 /* XXX do I need to enable SIGWINCH handling in ncurses myself? */
280 
281 /* check term size */
282 if(COLS<80 || LINES<13)
283   {
284   ui_end();
285   fprintf(stderr,"aylet: "
286   		"sorry, need a terminal with at least 80 cols and 13 lines.\n");
287   exit(1);
288   }
289 
290 frame_y=(LINES-13)/2;
291 
292 clearscreen();
293 }
294 
295 
ui_end(void)296 void ui_end(void)
297 {
298 if(!use_ui || !used_curses_ui) return;
299 
300 clearscreen();
301 move(LINES-1,0);
302 refresh();
303 nodelay(stdscr,0);
304 echo(); nocbreak();
305 endwin();
306 putchar('\n');
307 
308 /* in case of error exit, since stderr will be used immediately after... */
309 fflush(stdout);
310 }
311