1 /*
2 * YASR ("Yet Another Screen Reader") is an attempt at a lightweight,
3 * portable screen reader.
4 *
5 * Copyright (C) 2001-2003 by Michael P. Gorse. All rights reserved.
6 *
7 * YASR comes with ABSOLUTELY NO WARRANTY.
8 *
9 * This is free software, placed under the terms of the
10 * GNU Lesser General Public License, as published by the Free Software
11 * Foundation. Please see the file COPYING for details.
12 *
13 * Web Page: http://yasr.sf.net
14 *
15 * This software is maintained by:
16 * Michael P. Gorse <mgorse@users.sourceforge.net>
17 */
18
19 #include "yasr.h"
20
21 static void ui_saypos(int, int);
22
ui_funcman(int (* f)(int))23 void ui_funcman(int (*f) (int))
24 {
25 static void *fstack[8]; /* crappy stack, but who cares. */
26 static int nf = 0;
27
28 if (f)
29 {
30 fstack[nf++] = (void *) ui.func;
31 ui.func = f;
32 (*ui.func) (0);
33 } else
34 {
35 ui.func = (int (*)(int)) fstack[--nf];
36 }
37 }
38
39
rev_rttc(int * argp)40 /*ARGSUSED*/ void rev_rttc(int *argp)
41 {
42 int cr, cc;
43 int i;
44
45 if (ui.revmode)
46 {
47 cr = rev.cr;
48 cc = rev.cc;
49 } else
50 {
51 cr = win->cr;
52 cc = win->cc;
53 }
54 for (i = 0; i < cr; i++)
55 {
56 ui_sayline(i, 0);
57 }
58 ui_saylinepart(i, 0, cc, 0);
59 }
60
61
rev_rctb(int * argp)62 /*ARGSUSED*/ void rev_rctb(int *argp)
63 {
64 int i;
65 int cr, cc;
66
67 if (ui.revmode)
68 {
69 cr = rev.cr;
70 cc = rev.cc;
71 } else
72 {
73 cr = win->cr;
74 cc = win->cc;
75 }
76 ui_saylinepart(cr, cc, -1, 0);
77 for (i = cr + 1; i < win->rows; i++)
78 {
79 ui_sayline(i, 0);
80 }
81 }
82
83
rev_rs(int * argp)84 void rev_rs(int *argp)
85 {
86 int i;
87 int a, b;
88
89 if (argp)
90 {
91 a = *argp;
92 b = *(argp + 1);
93 } else
94 {
95 a = 0;
96 b = win->rows - 1;
97 }
98
99 for (i = a; i <= b; i++)
100 {
101 ui_sayline(i, 0);
102 }
103 }
104
105
rev_curpos(int * argp)106 /*ARGSUSED*/ void rev_curpos(int *argp)
107 {
108 ui_saypos(rev.cr, rev.cc);
109 }
110
111
ui_sayword(int cr,int cc)112 void ui_sayword(int cr, int cc)
113 {
114 chartype *c;
115 int cp;
116 int i = 0;
117
118 c = win->row[cr] + cc;
119 cp = cc;
120 while (cp && (*c & 0xff) > 32)
121 {
122 c--;
123 cp--;
124 }
125 if ((*c & 0xff) <= 32)
126 {
127 c++;
128 cp++;
129 }
130 while (cp < win->cols && (*c & 0xff) > 32)
131 {
132 cp++;
133 buf[i++] = realchar(*c++);
134 }
135 if (i)
136 {
137 speak((char *) buf, i);
138 }
139 }
140
141
rev_line(int * argp)142 void rev_line(int *argp)
143 {
144 int nr, nc;
145
146 if (ui.revmode)
147 {
148 nr = rev.cr;
149 nc = rev.cc;
150 } else
151 {
152 nr = win->cr;
153 nc = win->cc;
154 }
155 if (argp && (*argp & 0xff00) == 0x0100)
156 {
157 nr = *argp & 0xff;
158 } else if (argp)
159 {
160 nr += (*argp * (ui.num ? ui.num : 1));
161 } else if (ui.num)
162 {
163 nr = ui.num - 1;
164 }
165
166 if (nr < 0)
167 {
168 nr = 0;
169 tts_say(_("top"));
170 }
171 else if (nr >= win->rows)
172 {
173 nr = win->rows - 1;
174 tts_say(_("bottom"));
175 }
176 if (ui.revmode)
177 {
178 rev.cr = nr;
179 }
180
181 if (!argp || !rev.udmode || !(*(argp + 1)))
182 {
183 ui_sayline(nr, 1);
184 return;
185 }
186
187 switch (rev.udmode)
188 {
189 case 1:
190 ui_saychar(nr, nc);
191 break;
192 case 2:
193 ui_sayword(nr, nc);
194 break;
195 }
196 }
197
198
rev_word(int * argp)199 void rev_word(int *argp)
200 {
201 int nw;
202 int nr, nc;
203
204 if (ui.revmode)
205 {
206 nr = rev.cr;
207 nc = rev.cc;
208 } else
209 {
210 nr = win->cr;
211 nc = win->cc;
212 }
213 nw = argp ? *argp : 0;
214
215 if (nw > 0)
216 {
217 while (!cblank(nr, nc) && nc < win->cols)
218 {
219 nc++;
220 }
221 }
222 if (nw < 0)
223 {
224 while (nc && !cblank(nr, nc))
225 {
226 nc--;
227 }
228 }
229
230 while (nw > 0)
231 {
232 nw--;
233 for (;;)
234 {
235 if (++nc >= win->cols)
236 {
237 if (++nr == win->rows)
238 {
239 tts_say(_("bottom right"));
240 nr--;
241 nc--;
242 break;
243 }
244 nc = 0;
245 }
246 if (!cblank(nr, nc))
247 break;
248 }
249 }
250
251 while (nw < 0)
252 {
253 nw++;
254 for (;;)
255 {
256 if (--nc < 0)
257 {
258 if (--nr < 0)
259 {
260 tts_say(_("top left"));
261 nr = nc = 0;
262 break;
263 }
264 nc = win->cols - 1;
265 }
266 if (!cblank(nr, nc))
267 {
268 while (nc && !cblank(nr, nc - 1))
269 {
270 nc--;
271 }
272 break;
273 }
274 }
275 }
276 ui_sayword(nr, nc);
277 if (ui.revmode)
278 {
279 rev.cr = nr;
280 rev.cc = nc;
281 }
282 }
283
284
ui_sayphonetic(int row,int col)285 static void ui_sayphonetic(int row, int col)
286 {
287 tts_sayphonetic((char) win->row[row][col]);
288 }
289
290
rev_ch(int * argp)291 void rev_ch(int *argp)
292 {
293 int nr, nc;
294
295 if (ui.revmode)
296 {
297 nr = rev.cr;
298 nc = rev.cc;
299 } else
300 {
301 nr = win->cr;
302 nc = win->cc;
303 }
304
305 if (argp)
306 {
307 nc += (*argp * (ui.num ? ui.num : 1));
308 } else if (ui.num)
309 {
310 nc = ui.num - 1;
311 }
312 while (nc < 0)
313 {
314 nr--;
315 nc += win->cols;
316 }
317 while (nc >= win->cols)
318 {
319 nr++;
320 nc -= win->cols;
321 }
322 if (nr < 0)
323 {
324 nr = nc = 0;
325 tts_say(_("top left"));
326 }
327 else if (nr >= win->rows)
328 {
329 nr = win->rows - 1;
330 nc = win->cols - 1;
331 tts_say(_("bottom right"));
332 }
333 (rev.repeat & 1 && !argp ? ui_sayphonetic : ui_saychar) (nr, nc);
334 if (ui.revmode)
335 {
336 rev.cr = nr;
337 rev.cc = nc;
338 }
339 }
340
341
ui_ennum(int ch)342 int ui_ennum(int ch)
343 {
344 if (!ch)
345 {
346 ui.num = ui.abort = 0;
347 return (1);
348 } else if (ch < 0x0100 && isdigit(ch))
349 {
350 ui.num = ui.num * 10 + (ch - 48);
351 return (1);
352 }
353 ui_funcman(0);
354 if (ch == 27)
355 {
356 tts_say(_("Aborting."));
357 ui.abort = 1;
358 ui.num = 0;
359 }
360
361 return (2);
362 }
363
ui_build_str(int ch)364 int ui_build_str(int ch)
365 {
366 switch (ch)
367 {
368 case 0:
369 ui.strlen = ui.abort = 0;
370 return 1;
371 case 0x08:
372 case 0x7f:
373 if (ui.strlen)
374 {
375 tts_saychar(ui.str[--ui.strlen]);
376 }
377 return 1;
378 case 27: /* escape */
379 tts_say(_("Aborting."));
380 ui.abort = 1;
381 ui_funcman(0);
382 return 2;
383 case 13:
384 case 10:
385 ui.str[ui.strlen] = 0;
386 ui_funcman(0);
387 return 2;
388 default:
389 if (ui.strlen < sizeof(ui.str) - 1) /* ascii dep. */
390 {
391 ui.str[ui.strlen++] = ch;
392 }
393 return 1;
394 }
395 }
396
397
rev_searchline(int l,int c1,int c2,int reverse)398 static int rev_searchline(int l, int c1, int c2, int reverse)
399 {
400 int i, j, len;
401 int a, b, step;
402 chartype *cp;
403
404 len = strlen(rev.findbuf);
405 if (c2 == -1)
406 {
407 c2 = win->cols - len;
408 }
409 if (c2 < c1)
410 {
411 return (0);
412 }
413 if (reverse)
414 {
415 a = c2;
416 b = c1 - 1;
417 step = -1;
418 } else
419 {
420 a = c1;
421 b = c2 + 1;
422 step = 1;
423 }
424 for (i = a, cp = win->row[l] + a; i != b; i += step, cp += step)
425 {
426 for (j = 0; j < len; j++)
427 {
428 if (i + j < win->cols &&
429 toupper(realchar(*(cp + j))) != toupper(rev.findbuf[j]))
430 {
431 break;
432 }
433 }
434 if (j == len)
435 {
436 rev.cr = l;
437 rev.cc = i;
438 ui_saypos(rev.cr, rev.cc);
439 return (1);
440 }
441 }
442
443 return (0);
444 }
445
446
rev_searchtocursor(int * argp)447 /*ARGSUSED*/ void rev_searchtocursor(int *argp)
448 {
449 int i;
450
451 if (rev.cc && rev_searchline(rev.cr, 0, rev.cc - 1, 1))
452 {
453 return;
454 }
455 for (i = rev.cr - 1; i >= 0; i--)
456 {
457 if (rev_searchline(i, 0, -1, 1))
458 {
459 return;
460 }
461 }
462 tts_say(_("not found"));
463 }
464
rev_searchtoend(int * argp)465 /*ARGSUSED*/ void rev_searchtoend(int *argp)
466 {
467 int i;
468
469 if (rev_searchline(rev.cr, rev.cc + 1, -1, 0))
470 {
471 return;
472 }
473 for (i = rev.cr + 1; i < win->rows; i++)
474 {
475 if (rev_searchline(i, 0, -1, 0))
476 {
477 return;
478 }
479 }
480 tts_say(_("not found"));
481 }
482
483
rev_searchall(int * argp)484 /*ARGSUSED*/ static void rev_searchall(int *argp)
485 {
486 int i;
487
488 for (i = 0; i < win->rows; i++)
489 {
490 if (rev_searchline(i, 0, -1, 0))
491 {
492 return;
493 }
494 }
495 tts_say(_("not found"));
496 }
497
498
rev_find_aux(int ch)499 static int rev_find_aux(int ch)
500 {
501 if (!rev.meta)
502 {
503 switch (ch)
504 {
505 case 0: /* initialize */
506 rev.findbuflen = rev.meta = 0;
507 tts_say(_("Enter pattern to find"));
508 return (1);
509
510 case '>':
511 rev.findbuf[rev.findbuflen] = 0;
512 ui_funcman(0);
513 rev_searchtoend(NULL);
514 return (1);
515
516 case '<':
517 rev.findbuf[rev.findbuflen] = 0;
518 ui_funcman(0);
519 rev_searchtocursor(NULL);
520 return (1);
521
522 case '\\':
523 rev.meta = 1;
524 return (1);
525
526 case 0x08:
527 case 0x7f:
528 if (rev.findbuflen)
529 {
530 tts_saychar(rev.findbuf[--rev.findbuflen]);
531 }
532 return (1);
533
534 case 27: /* escape */
535 tts_say(_("Aborting."));
536 ui_funcman(0);
537 ui.abort = 1;
538 return 1;
539
540 case 13:
541 case 10:
542 rev.findbuf[rev.findbuflen] = 0;
543 ui_funcman(0);
544 rev_searchall(NULL);
545 return (1);
546 }
547 }
548 rev.meta = 0;
549 if (rev.findbuflen >= win->cols)
550 {
551 return (1);
552 }
553 rev.findbuf[rev.findbuflen++] = (char) ch;
554
555 return (1);
556 }
557
558
rev_find(int * argp)559 /*ARGSUSED*/ void rev_find(int *argp)
560 {
561 ui_funcman(&rev_find_aux);
562 }
563
564
rev_toline(int * argp)565 void rev_toline(int *argp)
566 {
567 int arg1 = *argp, arg2 = *(argp + 1), reading = 0;
568
569 if (arg1 < 0)
570 {
571 if (arg1 <= -win->rows)
572 {
573 arg1 = -win->rows + 1;
574 }
575 arg1 = win->rows + arg1;
576 } else
577 {
578 if (!arg1 || arg1 > win->rows)
579 {
580 arg1 = win->rows;
581 }
582 }
583
584 rev.cr = arg1 - 1;
585 if (!arg2)
586 {
587 return;
588 }
589
590 if (arg2 == 1 || arg2 == 3)
591 {
592 if (!rev.cr)
593 {
594 tts_say(_("top"));
595 }
596 else if (rev.cr == win->rows - 1)
597 {
598 tts_say(_("bottom"));
599 }
600 }
601
602 if (arg2 > 1)
603 {
604 reading = 1;
605 }
606 ui_sayline(rev.cr, reading);
607 }
608
609
rev_tocol(int * argp)610 void rev_tocol(int *argp)
611 {
612 int arg1 = *argp, arg2 = *(argp + 1), i, c = -1;
613
614 if (arg1 > 0xff)
615 {
616 arg1 -= 256;
617 c = 0;
618 for (i = 0; i <= win->cols; i++)
619 {
620 if (win->row[rev.cr][i] > ' ')
621 {
622 c = i;
623 }
624 }
625 }
626 if (arg1 < 0)
627 {
628 if (arg1 <= -win->cols)
629 {
630 arg1 = -win->cols + 1;
631 }
632 arg1 = win->cols + arg1;
633 } else if (!arg1 || arg1 > win->cols)
634 {
635 arg1 = win->cols;
636 }
637
638 if (c > 0 && arg1 > c)
639 {
640 rev.cc = c;
641 } else
642 {
643 rev.cc = arg1 - 1;
644 }
645 if (!arg2)
646 {
647 return;
648 }
649
650 if (arg2 >= 2)
651 {
652 if (!rev.cc)
653 {
654 tts_say(_("left"));
655 }
656 else if (rev.cc == win->cols - 1 || rev.cc == c)
657 {
658 tts_say(_("right"));
659 }
660 }
661 ui_saychar(rev.cr, rev.cc);
662 }
663
664
rev_main(int ch)665 static int rev_main(int ch)
666 {
667 int index;
668 Keybind *kf;
669
670 if (ch < 0x100 && isdigit(ch))
671 {
672 ui_funcman(&ui_ennum);
673 return (2);
674 }
675 index = kb_search(&rev.keymap, ch);
676 if (index == -1)
677 { /* invalid key */
678 rev.used = 0;
679 return (0);
680 }
681 kf = &rev.keymap.kb[index];
682 while (kf)
683 {
684 (*funcs[kf->index].f) (kf->argp);
685 kf = kf->next;
686 }
687 ui.num = 0;
688
689 return (1);
690 }
691
line_is_blank(int row,int c1,int c2)692 static int line_is_blank(int row, int c1, int c2)
693 {
694 int i;
695 int len;
696 chartype *rptr;
697
698 len = c2 - c1;
699 rptr = win->row[row] + c1;
700 for (i = 0; i < len; i++)
701 {
702 if (!y_isblank(*rptr++))
703 return 0;
704 }
705 return 1;
706 }
707
rev_nextpar(int * argp)708 void rev_nextpar(int *argp)
709 {
710 int i;
711
712 for (i = rev.cr; i < win->rows && !line_is_blank(i, 0, win->cols); i++);
713 for (; i < win->rows && line_is_blank(i, 0, win->cols); i++);
714 if (i == win->rows)
715 return;
716 rev.cr = i;
717 rev.cc = 0;
718 rev_rctb(NULL);
719 }
720
rev_prevpar(int * argp)721 void rev_prevpar(int *argp)
722 {
723 int i;
724
725 if (rev.cr == 0)
726 return;
727 for (i = rev.cr; i >= 0 && !line_is_blank(i, 0, win->cols); i--);
728 if (i >= rev.cr - 1)
729 {
730 for (; i >= 0 && line_is_blank(i, 0, win->cols); i--);
731 for (; i >= 0 && !line_is_blank(i, 0, win->cols); i--);
732 if (i < 0)
733 {
734 for (i = 0; i < win->rows && line_is_blank(i, 0, win->cols); i++);
735 if (i >= rev.cr)
736 return;
737 } else
738 i++;
739 } else
740 i++;
741 rev.cr = i;
742 rev.cc = 0;
743 rev_rctb(NULL);
744 }
745
ui_bypass(int * argp)746 /*ARGSUSED*/ void ui_bypass(int *argp)
747 {
748 ui.meta = 1;
749 }
750
751
ui_revtog(int * argp)752 /*ARGSUSED*/ void ui_revtog(int *argp)
753 {
754 ui.revmode ^= 1;
755 if (ui.revmode == 0)
756 {
757 tts_say(_("exit"));
758 ui.func = NULL;
759 return;
760 }
761 tts_say(_("review"));
762 ui.func = &rev_main;
763 rev.findbuflen = rev.meta = ui.num = 0;
764 if (!ui.rc_detached)
765 {
766 rev.cr = win->cr;
767 rev.cc = win->cc;
768 }
769 }
770
ui_detachtog(int * argp)771 /*ARGSUSED*/ void ui_detachtog(int *argp)
772 {
773 ui.rc_detached ^= 1;
774 if (ui.rc_detached)
775 {
776 tts_say(_("Detached"));
777 return;
778 }
779 rev.cr = win->cr;
780 rev.cc = win->cc;
781 tts_say(_("Attached"));
782 }
783
ui_routerc(int * argp)784 void ui_routerc(int *argp)
785 {
786 rev.cr = win->cr;
787 rev.cc = win->cc;
788 if (*argp)
789 {
790 if (*argp > 1)
791 {
792 tts_say(_("Cursor routed."));
793 }
794 if (*argp == 1 || *argp == 3)
795 {
796 ui_saypos(rev.cr, rev.cc);
797 }
798 }
799 }
800
801 /* Returns non-zero if the key has been processed. */
ui_keypress(int key)802 int ui_keypress(int key)
803 {
804 Keybind *kf;
805 int used = 0;
806 int index;
807
808 if (ui.func)
809 {
810 while ((used = (*ui.func) (key)) == 2)
811 {
812 continue;
813 }
814 }
815 if (used)
816 {
817 return (used);
818 }
819 index = kb_search(&ui.keymap, key);
820 if (index == -1)
821 {
822 if (!ui.revmode && kbuflen < 99)
823 kbuf[kbuflen++] = key;
824 return (ui.revmode);
825 }
826 kf = &ui.keymap.kb[index];
827 if (key == rev.lastkey)
828 {
829 rev.repeat++;
830 } else
831 {
832 rev.repeat = 0;
833 }
834 while (kf)
835 {
836 (*funcs[kf->index].f) (kf->argp);
837 kf = kf->next;
838 }
839 ui.num = 0;
840 rev.lastkey = key;
841
842 return (1);
843 }
844
845
ui_saychar(int row,int col)846 void ui_saychar(int row, int col)
847 {
848 tts_saychar((char) win->row[row][col]);
849 }
850
851
ui_sayblankline()852 static void ui_sayblankline()
853 {
854 tts_say(_("blank"));
855 }
856
ui_saylinepart(int row,int c1,int c2,int say_blank)857 void ui_saylinepart(int row, int c1, int c2, int say_blank)
858 {
859 int blank = 1;
860 int len;
861 chartype *rptr;
862 int i;
863
864 if (c2 == -1)
865 {
866 c2 = win->cols;
867 }
868 len = c2 - c1;
869 rptr = win->row[row] + c1;
870 for (i = 0; i < len; i++)
871 {
872 buf[i] = realchar(*(rptr++));
873 if (buf[i] != 32)
874 blank = 0;
875 }
876 if (blank)
877 {
878 if (say_blank)
879 {
880 ui_sayblankline();
881 }
882 return;
883 }
884 speak((char *) buf, len);
885 }
886
ui_sayline(int row,int say_blank)887 void ui_sayline(int row, int say_blank)
888 {
889 ui_saylinepart(row, 0, win->cols, say_blank);
890 }
891
ui_curpos(int * argp)892 /*ARGSUSED*/ void ui_curpos(int *argp)
893 {
894 ui_saypos(win->cr, win->cc);
895 }
896
897
ui_silence(int * argp)898 void ui_silence(int *argp)
899 {
900 tts_silence();
901 if (argp && *argp)
902 {
903 ui.silent++;
904 }
905 }
906
907
ui_saypos(int row,int col)908 static void ui_saypos(int row, int col)
909 {
910 char buf[20];
911
912 (void) sprintf(buf, "c%dl%d", col + 1, row + 1);
913 tts_say(buf);
914 }
915
916
uinit()917 void uinit()
918 {
919 (void) memset(&ui, 0, sizeof(ui));
920 ui.minrc = 4;
921 ui.curtrack = 2;
922 }
923
924
ui_kbwiz(int * argp)925 /*ARGSUSED*/ void ui_kbwiz(int *argp)
926 {
927 ui_funcman(&kbwiz);
928 }
929
930
ui_optmenu(int * argp)931 /*ARGSUSED*/ void ui_optmenu(int *argp)
932 {
933 ui_funcman(&optmenu);
934 }
935
936
ui_addstr(int ch)937 int ui_addstr(int ch)
938 {
939 switch (ch)
940 {
941 case 27:
942 tts_say(_("Aborting."));
943 ui_funcman(0);
944 /*FALLTHROUGH*/ case 0:
945 ui.buflen = 0;
946 break;
947
948 case 13:
949 ui_funcman(0);
950 return (2);
951
952 default:
953 if (ui.buflen < 99)
954 {
955 ui.buf[ui.buflen++] = ch;
956 }
957 }
958
959 return (1);
960 }
961
962
ui_opt_set(int * argp)963 void ui_opt_set(int *argp)
964 {
965 int t;
966
967 if (!argp)
968 {
969 tts_say(_("Error; no option to set. Fix keybinding in yasr.conf."));
970 return;
971 }
972 switch (opt[*argp].type)
973 {
974 case OT_STR:
975 opt_set(*argp, argp + 1);
976 break;
977
978 case OT_INT:
979 t = (opt_getval(*argp, 0) + 1) % (opt[*argp].v.val_int.max + 1);
980 opt_set(*argp, &t);
981 break;
982 case OT_ENUM:
983 t = (opt_getval(*argp, 0) + 1) % (opt[*argp].v.enum_max + 1);
984 opt_set(*argp, &t);
985 break;
986 default:
987 tts_say(_("error in keybinding: unsupported option type"));
988 break;
989 }
990 opt_say(*argp, 0);
991 }
992
993
ui_bol(int * argp)994 /*ARGSUSED*/ void ui_bol(int *argp)
995 {
996 for (rev.cc = 0; rev.cc < win->cols; rev.cc++)
997 {
998 if (!cblank(rev.cr, rev.cc))
999 {
1000 break;
1001 }
1002 }
1003 if (rev.cc == win->cols)
1004 {
1005 rev.cc = 0;
1006 }
1007 if (!cblank(rev.cr, rev.cc))
1008 {
1009 ui_sayword(rev.cr, rev.cc);
1010 }
1011 }
1012
1013
ui_eol(int * argp)1014 /*ARGSUSED*/ void ui_eol(int *argp)
1015 {
1016 for (rev.cc = win->cols - 1; rev.cc; rev.cc--)
1017 {
1018 if (!cblank(rev.cr, rev.cc))
1019 {
1020 break;
1021 }
1022 }
1023 if (!cblank(rev.cr, rev.cc))
1024 {
1025 ui_sayword(rev.cr, rev.cc);
1026 }
1027 }
1028
1029
ui_sayascii(int * argp)1030 /*ARGSUSED*/ void ui_sayascii(int *argp)
1031 {
1032 int cr;
1033 int cc;
1034
1035 if (ui.revmode)
1036 {
1037 cr = rev.cr;
1038 cc = rev.cc;
1039 } else
1040 {
1041 cr = win->cr;
1042 cc = win->cc;
1043 }
1044 tts_say_printf("%d", win->row[cr][cc] & 0xff);
1045 }
1046