* Wrote about that one time I got really drunk and tried to put every global variable...
[pineappletracker.git] / gui.c
blob80226954eb6db6878e68aa9e907f5184d9b0f18b
1 /* vi:set ts=4 sts=4 sw=4 noexpandtab: */
3 /* welcome to gui.c, enjoy your stay 8-) */
5 #include "pineapple.h"
6 #include "gui.h"
8 /* */
9 // ** LOCAL VARS ** //
10 /* */
11 char *dispmesg = "";
13 static char *notenames[] = {"C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "H-"};
15 /* */
16 // ** LOCAL FUNCTIONS ** //
17 /* */
18 int _char2int(char ch);
19 void _display(void);
21 /* */
22 // ** END LOCAL DECLARATIONS ** //
23 /* */
25 char cmdstr[500] = "";
27 int disptick = 0;
28 int currmode = PM_NORMAL;
29 int octave = 4;
30 int songlen = 1;
31 int tracklen = TRACKLEN;
32 int currtrack = 1;
33 int currinstr = 1;
34 int currtab = 0;
35 int saved = 1;
37 int step = 1;
39 int cmdrepeat = 0;
40 int cmdrepeatnum = 1;
41 int lastrepeat = 1;
43 // 0 is like a blank command
44 char *validcmds = "0dfi@smtvw~+=*";
46 /*char *keymap[2] = {
47 ";oqejkixdbhmwnvsz",
48 "'2,3.p5y6f7gc9r0l/="
49 };*/
51 char *keymap[2] = {
52 "zsxdcvgbhnjm,l.;/",
53 "q2w3er5t6y7ui9o0p"
56 /* hexinc and hexdec wrap around */
57 int hexinc(int x){
58 return (x >= 0 && x <= 14)? x+1 : 0;
60 int hexdec(int x){
61 return (x >= 1 && x <= 15)? x-1 : 15;
64 /* Wait for the next keyboard char and return it.
65 * This stops the screen from being updated. */
66 char nextchar(){
67 char ch;
68 ch = getch();
69 while (ch == ERR){
70 ch = getch();
71 if(ch != ERR ){
72 return ch;
74 usleep(10000);
76 return ch;
79 int _char2int(char ch){
80 if(isdigit(ch)){
81 return (int)ch - '0';
83 return -1;
86 int freqkey(int c){
87 char *s;
88 int f = -1;
90 if(c == '-' || c == KEY_DC) return 0;
91 if(c > 0 && c < 256){
92 s = strchr(keymap[0], c);
93 if(s){
94 f = (s - (keymap[0])) + octave * 12 + 1;
95 }else{
96 s = strchr(keymap[1], c);
97 if(s){
98 f = (s - (keymap[1])) + octave * 12 + 12 + 1;
102 if(f > 12 * 9 + 1) return -1;
103 return f;
106 void initsonglines(void){
107 for(int i=0; i < songlen; i++){
108 memmove(&song[i + 0], &song[i + 1], sizeof(struct songline) * (songlen - i - 1));
109 if(i < 4){
110 song[0].track[i] = 0x000;
111 song[0].transp[i] = 0x000;
114 songlen = 1;
117 void inittracks(void){
118 for(int i=0; i < 256; i++){
119 for(int j=0; j < TRACKLEN; j++){
120 track[i].line[j].note = 0x0000;
121 track[i].line[j].instr = 0x0000;
122 for(int k=0; k < 2; k++){
123 track[i].line[j].cmd[k] = 0x0000;
124 track[i].line[j].param[k] = 0x0000;
130 void initinstrs(void){
131 for(int i=1; i < 256; i++){
132 instrument[i].length = 1;
133 instrument[i].line[0].cmd = '0';
134 instrument[i].line[0].param = 0;
138 void readsong(int pos, int ch, u8 *dest){
139 dest[0] = song[pos].track[ch];
140 dest[1] = song[pos].transp[ch];
143 void readtrack(int num, int pos, struct trackline *tl){
144 tl->note = track[num].line[pos].note;
145 tl->instr = track[num].line[pos].instr;
146 tl->cmd[0] = track[num].line[pos].cmd[0];
147 tl->cmd[1] = track[num].line[pos].cmd[1];
148 tl->param[0] = track[num].line[pos].param[0];
149 tl->param[1] = track[num].line[pos].param[1];
152 void readinstr(int num, int pos, u8 *il){
153 if(pos >= instrument[num].length){
154 il[0] = 0;
155 il[1] = 0;
156 }else{
157 il[0] = instrument[num].line[pos].cmd;
158 il[1] = instrument[num].line[pos].param;
162 void exitgui(){
163 endwin();
166 void initgui(){
167 initscr();
169 //if(setlocale(LC_CTYPE,"en_US.utf8") != NULL) setdisplay("UTF-8 enabled!");
171 // don't send newline on Enter key, and don't echo chars to the screen.
172 nonl();
173 noecho();
175 // make sure behaviour for special keys like ^H isn't overridden
176 keypad(stdscr, FALSE);
178 // nodelay() makes getch() non-blocking. This will cause the cpu to spin
179 // whenever we use getch() in a loop. This is necessary so the screen will
180 // update when you aren't pressing keys. halfdelay()'s minimum timeout time
181 // is one tenth of a second, which is too long for our purposes.
183 // Right now we are calling usleep() whenever we use getch() in a loop so
184 // the cpu won't spin. This solution isn't the best, for three reasons:
185 // 1. We're still wasting a little bit of cpu!!!!!
186 // 2. It is possible to enter keys faster than the usleep time. It's
187 // especially easy to do this by setting your key repeat rate really
188 // high and moving up or down, and the screen will lag a little.
189 // 3. nextchar() prevents the screen from being updated.
191 // Because of these two small problems, maybe we should eventually use
192 // keyboard interrupts to trigger gui events. I haven't done any research
193 // on that yet.
194 nodelay(stdscr, TRUE);
196 initinstrs();
198 atexit(exitgui);
201 void drawsonged(int x, int y, int height){
202 int i, j;
203 char buf[1024];
204 //NODE *match;
206 if(songy < songoffs) songoffs = songy;
207 if(songy >= songoffs + height) songoffs = songy - height + 1;
209 for(i = 0; i < songlen; i++){
210 if(i >= songoffs && i - songoffs < height){
211 move(y + i - songoffs, x + 0);
212 if(i == songy) attrset(A_BOLD);
214 snprintf(buf, sizeof(buf), "%02x", i);
216 if(i == 0){ addch(ACS_ULCORNER); }
217 else if(i == songlen-1){ addch(ACS_LLCORNER); }
218 else if(i%4 == 0){ addch(ACS_LTEE); }
219 else if(i < songlen-1){ addch(ACS_VLINE); }
220 addch(' ');
222 // should this line be highlighted?
223 //if( (match = list_contains(highlightlines, findu8, &i)) ){
224 if( currtab == 0 && currmode == PM_VISUALLINE &&
225 ((i <= highlight_firstline && i >= highlight_lastline)
226 || (i >= highlight_firstline && i <= highlight_lastline)) ){
227 attrset(A_REVERSE);
230 addstr(buf);
231 for(j = 0; j < 4; j++){
232 snprintf(buf, sizeof(buf), "%02x:%02x", song[i].track[j], song[i].transp[j]);
233 addstr(buf);
234 if(j != 3) addch(' ');
236 if(playsong && songpos == (i + 1)){
237 attrset(A_STANDOUT);
238 addch('*');
240 attrset(A_NORMAL);
245 void drawtracked(int x, int y, int height){
246 u8 i, j;
247 char buf[1024];
249 if(tracky < trackoffs) trackoffs = tracky;
250 if(tracky >= trackoffs + height) trackoffs = tracky - height + 1;
252 for(i = 0; i < tracklen; i++){
253 if(i >= trackoffs && i - trackoffs < height){
254 move(y + i - trackoffs, x + 0);
255 if(i == tracky) attrset(A_BOLD);
257 snprintf(buf, sizeof(buf), "%02x", i);
258 addstr(buf);
260 if(i == 0){ addch(ACS_LLCORNER); }
261 else if(i == 1){ addch(ACS_ULCORNER); }
262 else if(i == tracklen-1){ addch(ACS_LLCORNER); }
263 else if(i%4 == 0){ addch(ACS_LTEE); }
264 else if(i < tracklen-1){ addch(ACS_VLINE); }
265 addch(' ');
267 // should this line be highlighted?
268 //if( (match = list_contains(highlightlines, findu8, &i)) ){
269 if(currtab == 1 && currmode == PM_VISUALLINE
270 && ((i <= highlight_firstline && i >= highlight_lastline)
271 || (i >= highlight_firstline && i <= highlight_lastline)) ){
272 attrset(A_REVERSE);
275 if (currtab == 1 && currmode == PM_VISUAL)
276 attrset(A_REVERSE);
278 if(track[currtrack].line[i].note){
279 snprintf(buf, sizeof(buf), "%s%d",
280 notenames[(track[currtrack].line[i].note - 1) % 12],
281 (track[currtrack].line[i].note - 1) / 12);
282 }else{
283 snprintf(buf, sizeof(buf), "---");
285 addstr(buf);
286 snprintf(buf, sizeof(buf), " %02x", track[currtrack].line[i].instr);
287 addstr(buf);
288 for(j = 0; j < 2; j++){
289 if(track[currtrack].line[i].cmd[j]){
290 snprintf(buf, sizeof(buf), " %c%02x",
291 track[currtrack].line[i].cmd[j],
292 track[currtrack].line[i].param[j]);
293 }else{
294 snprintf(buf, sizeof(buf), " ...");
296 addstr(buf);
298 if(playtrack && ((i + 1) % tracklen) == trackpos){
299 attrset(A_STANDOUT);
300 addch('*');
302 attrset(A_NORMAL);
307 void drawinstred(int x, int y, int height){
308 u8 i;
309 char buf[1024];
311 if(instry >= instrument[currinstr].length) instry = instrument[currinstr].length - 1;
313 if(instry < instroffs) instroffs = instry;
314 if(instry >= instroffs + height) instroffs = instry - height + 1;
316 for(i = 0; i < instrument[currinstr].length; i++){
317 if(i >= instroffs && i - instroffs < height){
318 move(y + i - instroffs, x + 0);
319 if(i == instry) attrset(A_BOLD);
321 snprintf(buf, sizeof(buf), "%02x", i);
322 addstr(buf);
324 if(i == 0){ addch(ACS_LLCORNER); }
325 else if(i == 1){ addch(ACS_ULCORNER); }
326 else if(i == instrument[currinstr].length-1){ addch(ACS_LLCORNER); }
327 else if(i < instrument[currinstr].length-1){ addch(ACS_VLINE); }
328 addch(' ');
330 // should this line be highlighted?
331 //if( (match = list_contains(highlightlines, findu8, &i)) ){
332 if( currtab == 2 && currmode == PM_VISUALLINE &&
333 ((i <= highlight_firstline && i >= highlight_lastline)
334 || (i >= highlight_firstline && i <= highlight_lastline)) ){
335 attrset(A_REVERSE);
338 snprintf(buf, sizeof(buf), "%c ", instrument[currinstr].line[i].cmd);
339 addstr(buf);
340 if(instrument[currinstr].line[i].cmd == '+' || instrument[currinstr].line[i].cmd == '='){
341 if(instrument[currinstr].line[i].param){
342 snprintf(buf, sizeof(buf), "%s%d",
343 notenames[(instrument[currinstr].line[i].param - 1) % 12],
344 (instrument[currinstr].line[i].param - 1) / 12);
345 }else{
346 snprintf(buf, sizeof(buf), "---");
348 }else{
349 snprintf(buf, sizeof(buf), "%02x", instrument[currinstr].line[i].param);
351 addstr(buf);
352 attrset(A_NORMAL);
357 /* main input loop */
358 void handleinput(){
359 int c;
361 /*if(currmode == PM_NORMAL){*/
362 if((c = getch()) != ERR){
364 /* Repeat */
365 if(isdigit(c)){
366 if(!cmdrepeat){
367 cmdrepeat = 1;
368 cmdrepeatnum = _char2int(c);
369 }else{
370 cmdrepeatnum = (cmdrepeatnum*10) + _char2int(c);
372 }else{
373 normalmode(c);
376 usleep(10000);
379 void setdisplay(char *str){
380 disptick = 350;
381 dispmesg = str;
384 // display dispmesg in the center of the screen
385 void _display(void){
386 int cx = (getmaxx(stdscr)/2)-(strlen(dispmesg)/2)-1;
387 int cy = getmaxy(stdscr)/2;
389 mvaddch(cy-1, cx, ACS_ULCORNER);
390 for(int i=cx+1; i<cx+strlen(dispmesg)+1; i++)
391 mvaddch(cy-1, i, ACS_HLINE);
392 mvaddch(cy-1, cx+strlen(dispmesg)+1, ACS_URCORNER);
394 mvaddch(cy, cx, ACS_VLINE);
395 mvaddstr(cy, cx+1, dispmesg);
396 mvaddch(cy, cx+strlen(dispmesg)+1, ACS_VLINE);
398 mvaddch(cy+1, cx, ACS_LLCORNER);
399 for(int i=cx+1; i<cx+strlen(dispmesg)+1; i++)
400 mvaddch(cy+1, i, ACS_HLINE);
401 mvaddch(cy+1, cx+strlen(dispmesg)+1, ACS_LRCORNER);
404 void drawgui(){
405 char buf[1024];
406 int lines = LINES;
407 int songcols[] = {0, 1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22};
408 int trackcols[] = {0, 2, 4, 5, 7, 8, 9, 11, 12, 13};
409 int instrcols[] = {0, 2, 3};
410 u8 tempo;
412 erase();
413 attrset(A_UNDERLINE);
414 mvaddstr(0, 0, "PINEAPPLEtRACKER");
415 attrset(A_NORMAL);
417 // display track num
418 mvaddch(0, 31, ACS_ULCORNER);
419 snprintf(buf, sizeof(buf), "%02x{}", currtrack);
420 mvaddstr(0, 32, buf);
421 drawtracked(29, 1, lines - 2);
423 // display instrument num
424 mvaddch(0, 51, ACS_ULCORNER);
425 snprintf(buf, sizeof(buf), "%02x[]", currinstr);
426 mvaddstr(0, 52, buf);
427 drawinstred(49, 1, lines - 2);
429 mvaddstr(1, 0, "Song");
430 drawsonged(0, 1, lines - 2);
432 // just a wild guess here..
433 tempo = callbacktime * (-1) + 300;
434 // display tempo
435 mvaddch(0, 17, ACS_DEGREE);
436 snprintf(buf, sizeof(buf), "%d()", tempo);
437 mvaddstr(0, 18, buf);
439 // display octave
440 mvaddch(0, 24, ACS_PI);
441 snprintf(buf, sizeof(buf), "%d<>", octave);
442 mvaddstr(0, 25, buf);
444 // display step amount
445 mvaddstr(0, 60, "step -=");
446 snprintf(buf, sizeof(buf), "%0x", step);
447 mvaddstr(0, 68, buf);
449 // display comment
450 mvaddstr(2, 60, "comment:");
451 snprintf(buf, sizeof(buf), "%s", comment);
452 mvaddstr(3, 60, buf);
454 if(currmode == PM_NORMAL){
455 mvaddstr(getmaxy(stdscr)-1, 0, filename);
456 if(!saved && currmode != PM_INSERT){
457 addstr(" [+]");
458 infinitemsg = NULL;
462 if(disptick > 0){
463 _display();
464 disptick--;
467 if(currmode == PM_INSERT){
468 infinitemsg = NULL;
470 move(getmaxy(stdscr)-1,0);
471 clrtoeol();
472 mvaddstr(getmaxy(stdscr)-1, 0, "-- INSERT --");
473 }else if(currmode == PM_VISUAL){
474 infinitemsg = NULL;
476 move(getmaxy(stdscr)-1,0);
477 clrtoeol();
478 mvaddstr(getmaxy(stdscr)-1, 0, "-- VISUAL --");
479 }else if(currmode == PM_VISUALLINE){
480 infinitemsg = NULL;
482 move(getmaxy(stdscr)-1,0);
483 clrtoeol();
484 mvaddstr(getmaxy(stdscr)-1, 0, "-- VISUAL LINE --");
485 }else if(currmode == PM_JAMMER){
486 infinitemsg = NULL;
488 move(getmaxy(stdscr)-1,0);
489 clrtoeol();
490 mvaddstr(getmaxy(stdscr)-1, 0, "-- JAMMER --");
491 }else if(currmode == PM_CMDLINE){
492 infinitemsg = NULL;
494 move(getmaxy(stdscr)-1,0);
495 clrtoeol();
496 mvaddstr(getmaxy(stdscr) - 1, 0, cmdstr);
497 }else if(infinitemsg != NULL){
498 move(getmaxy(stdscr)-1,0);
499 clrtoeol();
500 mvaddstr(getmaxy(stdscr) - 1, 0, infinitemsg);
503 switch(currtab){
504 case 0:
505 move(1 + songy - songoffs, 0 + 4 + songcols[songx]);
506 break;
507 case 1:
508 move(1 + tracky - trackoffs, 29 + 4 + trackcols[trackx]);
509 break;
510 case 2:
511 move(1 + instry - instroffs, 49 + 4 + instrcols[instrx]);
512 break;
515 refresh();
517 if(disptick > 0){
518 disptick--;
522 void guiloop(){
523 #ifndef WINDOWS
524 // don't treat the escape key like a meta key
525 ESCDELAY = 50;
526 #endif
527 for(;;){
528 drawgui();
529 handleinput();