/* This program will print out exercise lines to the display, and then check for correct typing. */ /* Ported to the Atari-ST by Randy Hosler on May 16, 1992. Ported from unix sources, original author unknown. Compiled with HSC 1.33 and MiNT libs. cc typist.c libm1.a libm.a Looks for *.typ files in d:\sozobon\lib\typist\ or current dir. ( see find_lesson() ) */ #include #include #include #include #include #include #include typedef unsigned char BoolType; #if !defined(FALSE) #define FALSE 0 #define TRUE 1 #endif #if !defined(NULL) #define NULL 0 #endif #define CLRSCREEN Cconws("\033H\033J") #define BEEP Cconout('\007') #define ASCII_ESC 27 #define ASCII_BS 8 #define ASCII_RUBOUT 127 #define STR_SIZE 256 typedef char StrType[STR_SIZE + 1]; #define MAX_LINES 1500 char *cached_lines[MAX_LINES]; int linesInLesson; int exerciseStart; int newStart; FILE *lesson_file; StrType current_line; mvcur(r,c) int r,c; { Cconout('\033'); Cconout('Y'); Cconout(r+040); Cconout(c+040); } wait_user() /* Get any response from user before proceeding */ { mvcur(23, 0); Cconws("Press any key to continue..."); Cnecin(); } void get_end_line() /* Read end of line, should be smarter... */ { Cnecin(); } int cleanup() /* cleanup routine, esp. SIGINT */ { Cconws("\r\n"); exit(0); } char *git_lesson() /* Ask user for desired lesson */ { static StrType response; mvcur(0,0); Cconws(" Several lessons are available:\r\n"); Cconws(" Quick QWERTY course (q1 - q5)\r\n"); Cconws(" Long QWERTY course (r1 - r14)\r\n"); Cconws(" QWERTY touch typing (t1 - t16)\r\n"); Cconws(" Yet Another QWERTY (v1 - v20)\r\n"); Cconws(" QWERTY Review (u1 - u13)\r\n"); Cconws(" Dvorak touch typing (d1 - d14)\r\n"); Cconws(" Typing drills (m1 - m11)\r\n"); Cconws(" Speed drills (s1 - s4)\r\n"); Cconws(" Calculator keypad (n1 - n3)\r\n"); mvcur(11,0); Cconws(" Press return to exit or else type the desired lesson name: "); gets(response); if (strlen(response) == 0) cleanup(); return response; } BoolType get_lesson_line() /* Read line of input from user, in canonical mode */ { if (feof(lesson_file)) return FALSE; if (fgets(current_line, STR_SIZE, lesson_file) == NULL) current_line[0] = '\0'; else { if (current_line[strlen(current_line) - 1] == '\n') current_line[strlen(current_line) - 1] = '\0'; } return TRUE; } BoolType find_lesson(name) /*locate lesson leave file open so next line begins*/ char *name; { StrType fullName; StrType target; StrType response; BoolType done = FALSE; sprintf(fullName, "d:\\sozobon\\lib\\typist\\%c.typ", name[0]); sprintf(target, "*%c%s*", toupper(name[0]), &name[1]); lesson_file = fopen(fullName, "r"); if (lesson_file == NULL) { sprintf(fullName, "%c.typ", name[0]); /* try current dir */ lesson_file = fopen(fullName, "r"); if (lesson_file == NULL) { sprintf(response, "Can't open file %s\n", fullName); Cconws("\r\n"); Cconws(response); wait_user(); return FALSE; } } while (!done) { if (!get_lesson_line()) break; if (strncmp(current_line, target, 5) == 0) done = TRUE; } if (!done) { sprintf(response, "Lesson %s is not available.\n", &name[1]); Cconws("\r\n"); Cconws(response); wait_user(); return FALSE; } linesInLesson = 0; done = FALSE; while (!done) { if (!get_lesson_line()) { done = TRUE; break; } if (current_line[0] == '*') { done = TRUE; break; } if (linesInLesson == MAX_LINES) { } if (cached_lines[linesInLesson] != NULL) { free(cached_lines[linesInLesson]); cached_lines[linesInLesson] = NULL; } cached_lines[linesInLesson] = (char *)malloc(strlen(current_line) + 1); if (cached_lines[linesInLesson] == NULL) { fprintf(stderr, "malloc() failure\n"); exit(1); } strcpy(cached_lines[linesInLesson], current_line); linesInLesson ++; } return TRUE; } void get_exercise(startLine, kind, newStart) /*get length and kind of exercise*/ int startLine; char *kind; int *newStart; { char *thisLine; int lineLength; *newStart = startLine; *kind = 'I'; /* default... */ /* Determine end of exercise */ while (TRUE) { if (*newStart >= linesInLesson) break; /* done */ thisLine = cached_lines[*newStart]; lineLength = strlen(thisLine); if (thisLine[lineLength - 2] == '\\') { /* expected terminator */ *kind = thisLine[lineLength - 1]; thisLine[lineLength - 2] = '\0'; /* remove */ (*newStart) ++; break; } if (thisLine[lineLength - 1] == '\\') /* compatibility with BASIC */ thisLine[lineLength - 1] = '\0'; /* also remove */ (*newStart) ++; } } void displaySpeed(totalChars, elapsed, errs) /* show how fast you were */ int totalChars; long elapsed; int errs; { elapsed = elapsed / 200; { double testTime = (double)elapsed / (double)60.0; /* time in minutes */ double words = (double)totalChars / 5.0; double speed = words / testTime; double adjustedSpeed; StrType message; words -= errs; adjustedSpeed = words / testTime; mvcur(19, 19); sprintf(message, "raw speed = %f wpm", speed); Cconws(message); mvcur(20, 19); sprintf(message, "adjusted speed = %f wpm", adjustedSpeed); Cconws(message); mvcur(21, 19); sprintf(message, " with %f%s errors", (double)100.0 * errs / totalChars, "%"); Cconws(message); wait_user(); } } void do_drill() /* \D, a drill */ { int tries; int errors; int totalChars; int i, j; char *thisLine; int thisLength; char ch; long startTime, endTime; tries = 0; do { /* a try */ totalChars = 0; errors = 0; tries ++; /* display drill pattern */ for (i = exerciseStart; i < newStart; i ++) { mvcur(3 + (i - exerciseStart) * 2, 0); Cconws(cached_lines[i]); totalChars += strlen(cached_lines[i]) + 1; /* +1 for NULL */ } /* run the drill */ for (i = exerciseStart; i < newStart; i ++) { thisLine = cached_lines[i]; thisLength = strlen(thisLine); mvcur(4 + (i - exerciseStart) * 2, 0); for (j = 0; j < thisLength; j ++) { ch = Cnecin(); if (i == exerciseStart && j == 0) startTime = clock(); /* timer doesn't run till first stroke */ if (ch == ASCII_ESC) { CLRSCREEN; goto ALL_DONE; } if (ch != thisLine[j]) { Cconout('X'); BEEP; errors ++; } else Cconout(ch); } get_end_line(); } endTime = clock(); displaySpeed(totalChars, endTime - startTime, errors); CLRSCREEN; if (errors > 0 && tries < 3) { mvcur(0, 0); Cconws("Try again..."); } tries ++; } while (errors > 0 && tries < 4); ALL_DONE:; } void do_para() /* P, practice paragraph */ { int i, j; char *thisLine; int thisLength; char ch; int totalChars = 0; int errors = 0; long startTime, endTime; /* print out practice text */ for (i = exerciseStart; i < newStart; i ++) { mvcur(3 + (i - exerciseStart), 0); Cconws(cached_lines[i]); totalChars += strlen(cached_lines[i]); } mvcur(3, 0); for (i = exerciseStart; i < newStart; i ++) { mvcur(3 + (i - exerciseStart), 0); thisLine = cached_lines[i]; thisLength = strlen(thisLine); for (j = 0; j < thisLength; j ++) { ch = Cnecin(); if (i == exerciseStart && j == 0) startTime = clock(); /* timer doesn't run till first stroke */ if (ch == ASCII_ESC) { CLRSCREEN; goto ALL_DONE; } /* Allow backspace */ if (j != 0 && (ch == ASCII_BS || ch == ASCII_RUBOUT)) { j --; mvcur(3 + (i - exerciseStart), j); Cconout(thisLine[j]); mvcur(3 + (i - exerciseStart), j); j --; continue; /* but notice that errors accumulate */ } if (ch != thisLine[j]) { mvcur(3 + (i - exerciseStart), j); Cconout(thisLine[j]); errors ++; } else mvcur(3 + (i - exerciseStart), j + 1); } get_end_line(); } endTime = clock(); displaySpeed(totalChars, endTime - startTime, errors); ALL_DONE:; } void do_type() /* T, type out a lesson */ { int i; CLRSCREEN; for (i = exerciseStart; i < newStart; i ++) { mvcur(1 + (i - exerciseStart), 0); Cconws(cached_lines[i]); } wait_user(); CLRSCREEN; } void do_instruction() /* I, instruction line */ { CLRSCREEN; mvcur(0, 0); Cconws(cached_lines[exerciseStart]); } void do_clear() /* B, clear screen */ { CLRSCREEN; mvcur(1, 29); Cconws(cached_lines[exerciseStart]); wait_user(); } void give_lesson() /* dance through lesson file */ { char kind; exerciseStart = 0; while (TRUE) { get_exercise(exerciseStart, &kind, &newStart); switch (kind) { case 'B': do_clear(); break; case 'P': do_para(); break; case 'T': do_type(); break; case 'D': do_drill(); break; case 'I': do_instruction(); break; default: fprintf(stderr, "Unknown exercise near line %d of this lesson\n", newStart - 1); exit(1); break; } exerciseStart = newStart; if (exerciseStart >= linesInLesson) break; } } int main(argc, argv) int argc; char *argv[]; { char *lessonName; int i; CLRSCREEN; for (i = 0; i < MAX_LINES; i ++) cached_lines[i] = NULL; /* Start and format screen. Open and read exercise file. Inquire for lesson to run */ while (TRUE) { CLRSCREEN; lessonName = git_lesson(); if (!find_lesson(lessonName)) continue; give_lesson(); } }