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