Linux 4.16.11
[linux/fpc-iii.git] / drivers / staging / speakup / main.c
blobcf125905977655b947c61636815c02c1fa355096
1 // SPDX-License-Identifier: GPL-2.0+
2 /* speakup.c
3 * review functions for the speakup screen review package.
4 * originally written by: Kirk Reiser and Andy Berdan.
6 * extensively modified by David Borowski.
8 ** Copyright (C) 1998 Kirk Reiser.
9 * Copyright (C) 2003 David Borowski.
12 #include <linux/kernel.h>
13 #include <linux/vt.h>
14 #include <linux/tty.h>
15 #include <linux/mm.h> /* __get_free_page() and friends */
16 #include <linux/vt_kern.h>
17 #include <linux/ctype.h>
18 #include <linux/selection.h>
19 #include <linux/unistd.h>
20 #include <linux/jiffies.h>
21 #include <linux/kthread.h>
22 #include <linux/keyboard.h> /* for KT_SHIFT */
23 #include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
24 #include <linux/input.h>
25 #include <linux/kmod.h>
27 /* speakup_*_selection */
28 #include <linux/module.h>
29 #include <linux/sched.h>
30 #include <linux/slab.h>
31 #include <linux/types.h>
32 #include <linux/consolemap.h>
34 #include <linux/spinlock.h>
35 #include <linux/notifier.h>
37 #include <linux/uaccess.h> /* copy_from|to|user() and others */
39 #include "spk_priv.h"
40 #include "speakup.h"
42 #define MAX_DELAY msecs_to_jiffies(500)
43 #define MINECHOCHAR SPACE
45 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
46 MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
47 MODULE_DESCRIPTION("Speakup console speech");
48 MODULE_LICENSE("GPL");
49 MODULE_VERSION(SPEAKUP_VERSION);
51 char *synth_name;
52 module_param_named(synth, synth_name, charp, 0444);
53 module_param_named(quiet, spk_quiet_boot, bool, 0444);
55 MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");
56 MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");
58 special_func spk_special_handler;
60 short spk_pitch_shift, synth_flags;
61 static u16 buf[256];
62 int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
63 int spk_no_intr, spk_spell_delay;
64 int spk_key_echo, spk_say_word_ctl;
65 int spk_say_ctrl, spk_bell_pos;
66 short spk_punc_mask;
67 int spk_punc_level, spk_reading_punc;
68 char spk_str_caps_start[MAXVARLEN + 1] = "\0";
69 char spk_str_caps_stop[MAXVARLEN + 1] = "\0";
70 const struct st_bits_data spk_punc_info[] = {
71 {"none", "", 0},
72 {"some", "/$%&@", SOME},
73 {"most", "$%&#()=+*/@^<>|\\", MOST},
74 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC},
75 {"delimiters", "", B_WDLM},
76 {"repeats", "()", CH_RPT},
77 {"extended numeric", "", B_EXNUM},
78 {"symbols", "", B_SYM},
79 {NULL, NULL}
82 static char mark_cut_flag;
83 #define MAX_KEY 160
84 static u_char *spk_shift_table;
85 u_char *spk_our_keys[MAX_KEY];
86 u_char spk_key_buf[600];
87 const u_char spk_key_defaults[] = {
88 #include "speakupmap.h"
91 /* Speakup Cursor Track Variables */
92 static int cursor_track = 1, prev_cursor_track = 1;
94 /* cursor track modes, must be ordered same as cursor_msgs */
95 enum {
96 CT_Off = 0,
97 CT_On,
98 CT_Highlight,
99 CT_Window,
100 CT_Max
103 #define read_all_mode CT_Max
105 static struct tty_struct *tty;
107 static void spkup_write(const u16 *in_buf, int count);
109 static char *phonetic[] = {
110 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
111 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
112 "papa",
113 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
114 "x ray", "yankee", "zulu"
117 /* array of 256 char pointers (one for each character description)
118 * initialized to default_chars and user selectable via
119 * /proc/speakup/characters
121 char *spk_characters[256];
123 char *spk_default_chars[256] = {
124 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
125 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
126 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
127 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
128 "control",
129 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
130 "tick",
131 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
132 "dot",
133 "slash",
134 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
135 "eight", "nine",
136 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
137 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
138 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
139 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
140 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
141 "caret",
142 "line",
143 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
144 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
145 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
146 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
147 /*127*/ "del", "control", "control", "control", "control", "control",
148 "control", "control", "control", "control", "control",
149 /*138*/ "control", "control", "control", "control", "control",
150 "control", "control", "control", "control", "control",
151 "control", "control",
152 /*150*/ "control", "control", "control", "control", "control",
153 "control", "control", "control", "control", "control",
154 /*160*/ "nbsp", "inverted bang",
155 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
156 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
157 /*172*/ "not", "soft hyphen", "registered", "macron",
158 /*176*/ "degrees", "plus or minus", "super two", "super three",
159 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
160 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
161 /*188*/ "one quarter", "one half", "three quarters",
162 "inverted question",
163 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
164 "A RING",
165 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
166 "E OOMLAUT",
167 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
168 "N TILDE",
169 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
170 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
171 "U CIRCUMFLEX",
172 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
173 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
174 /*230*/ "ae", "c cidella", "e grave", "e acute",
175 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
176 "i circumflex",
177 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
178 "o circumflex",
179 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
180 "u acute",
181 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
184 /* array of 256 u_short (one for each character)
185 * initialized to default_chartab and user selectable via
186 * /sys/module/speakup/parameters/chartab
188 u_short spk_chartab[256];
190 static u_short default_chartab[256] = {
191 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */
192 B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */
193 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */
194 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */
195 WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */
196 PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */
197 NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */
198 NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */
199 PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */
200 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */
201 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */
202 A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */
203 PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */
204 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */
205 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */
206 ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */
207 B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */
208 B_SYM, /* 135 */
209 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */
210 B_CAPSYM, /* 143 */
211 B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */
212 B_SYM, /* 151 */
213 B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */
214 B_SYM, /* 159 */
215 WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */
216 B_SYM, /* 167 */
217 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */
218 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */
219 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */
220 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */
221 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */
222 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */
223 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */
224 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */
225 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */
226 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */
227 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */
230 struct task_struct *speakup_task;
231 struct bleep spk_unprocessed_sound;
232 static int spk_keydown;
233 static u16 spk_lastkey;
234 static u_char spk_close_press, keymap_flags;
235 static u_char last_keycode, this_speakup_key;
236 static u_long last_spk_jiffy;
238 struct st_spk_t *speakup_console[MAX_NR_CONSOLES];
240 DEFINE_MUTEX(spk_mutex);
242 static int keyboard_notifier_call(struct notifier_block *,
243 unsigned long code, void *param);
245 static struct notifier_block keyboard_notifier_block = {
246 .notifier_call = keyboard_notifier_call,
249 static int vt_notifier_call(struct notifier_block *,
250 unsigned long code, void *param);
252 static struct notifier_block vt_notifier_block = {
253 .notifier_call = vt_notifier_call,
256 static unsigned char get_attributes(struct vc_data *vc, u16 *pos)
258 pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, 1);
259 return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8;
262 static void speakup_date(struct vc_data *vc)
264 spk_x = spk_cx = vc->vc_x;
265 spk_y = spk_cy = vc->vc_y;
266 spk_pos = spk_cp = vc->vc_pos;
267 spk_old_attr = spk_attr;
268 spk_attr = get_attributes(vc, (u_short *)spk_pos);
271 static void bleep(u_short val)
273 static const short vals[] = {
274 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
276 short freq;
277 int time = spk_bleep_time;
279 freq = vals[val % 12];
280 if (val > 11)
281 freq *= (1 << (val / 12));
282 spk_unprocessed_sound.freq = freq;
283 spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);
284 spk_unprocessed_sound.active = 1;
285 /* We can only have 1 active sound at a time. */
288 static void speakup_shut_up(struct vc_data *vc)
290 if (spk_killed)
291 return;
292 spk_shut_up |= 0x01;
293 spk_parked &= 0xfe;
294 speakup_date(vc);
295 if (synth)
296 spk_do_flush();
299 static void speech_kill(struct vc_data *vc)
301 char val = synth->is_alive(synth);
303 if (val == 0)
304 return;
306 /* re-enables synth, if disabled */
307 if (val == 2 || spk_killed) {
308 /* dead */
309 spk_shut_up &= ~0x40;
310 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));
311 } else {
312 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));
313 spk_shut_up |= 0x40;
317 static void speakup_off(struct vc_data *vc)
319 if (spk_shut_up & 0x80) {
320 spk_shut_up &= 0x7f;
321 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));
322 } else {
323 spk_shut_up |= 0x80;
324 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));
326 speakup_date(vc);
329 static void speakup_parked(struct vc_data *vc)
331 if (spk_parked & 0x80) {
332 spk_parked = 0;
333 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));
334 } else {
335 spk_parked |= 0x80;
336 synth_printf("%s\n", spk_msg_get(MSG_PARKED));
340 static void speakup_cut(struct vc_data *vc)
342 static const char err_buf[] = "set selection failed";
343 int ret;
345 if (!mark_cut_flag) {
346 mark_cut_flag = 1;
347 spk_xs = (u_short)spk_x;
348 spk_ys = (u_short)spk_y;
349 spk_sel_cons = vc;
350 synth_printf("%s\n", spk_msg_get(MSG_MARK));
351 return;
353 spk_xe = (u_short)spk_x;
354 spk_ye = (u_short)spk_y;
355 mark_cut_flag = 0;
356 synth_printf("%s\n", spk_msg_get(MSG_CUT));
358 speakup_clear_selection();
359 ret = speakup_set_selection(tty);
361 switch (ret) {
362 case 0:
363 break; /* no error */
364 case -EFAULT:
365 pr_warn("%sEFAULT\n", err_buf);
366 break;
367 case -EINVAL:
368 pr_warn("%sEINVAL\n", err_buf);
369 break;
370 case -ENOMEM:
371 pr_warn("%sENOMEM\n", err_buf);
372 break;
376 static void speakup_paste(struct vc_data *vc)
378 if (mark_cut_flag) {
379 mark_cut_flag = 0;
380 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));
381 } else {
382 synth_printf("%s\n", spk_msg_get(MSG_PASTE));
383 speakup_paste_selection(tty);
387 static void say_attributes(struct vc_data *vc)
389 int fg = spk_attr & 0x0f;
390 int bg = spk_attr >> 4;
392 if (fg > 8) {
393 synth_printf("%s ", spk_msg_get(MSG_BRIGHT));
394 fg -= 8;
396 synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));
397 if (bg > 7) {
398 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));
399 bg -= 8;
400 } else {
401 synth_printf(" %s ", spk_msg_get(MSG_ON));
403 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));
406 enum {
407 edge_top = 1,
408 edge_bottom,
409 edge_left,
410 edge_right,
411 edge_quiet
414 static void announce_edge(struct vc_data *vc, int msg_id)
416 if (spk_bleeps & 1)
417 bleep(spk_y);
418 if ((spk_bleeps & 2) && (msg_id < edge_quiet))
419 synth_printf("%s\n",
420 spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
423 static void speak_char(u16 ch)
425 char *cp;
426 struct var_t *direct = spk_get_var(DIRECT);
428 if (ch >= 0x100 || (direct && direct->u.n.value)) {
429 if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
430 spk_pitch_shift++;
431 synth_printf("%s", spk_str_caps_start);
433 synth_putwc_s(ch);
434 if (ch < 0x100 && IS_CHAR(ch, B_CAP))
435 synth_printf("%s", spk_str_caps_stop);
436 return;
439 cp = spk_characters[ch];
440 if (!cp) {
441 pr_info("%s: cp == NULL!\n", __func__);
442 return;
444 if (IS_CHAR(ch, B_CAP)) {
445 spk_pitch_shift++;
446 synth_printf("%s %s %s",
447 spk_str_caps_start, cp, spk_str_caps_stop);
448 } else {
449 if (*cp == '^') {
450 cp++;
451 synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp);
452 } else
453 synth_printf(" %s ", cp);
457 static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs)
459 u16 ch = ' ';
461 if (vc && pos) {
462 u16 w;
463 u16 c;
465 pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, 1);
466 w = scr_readw(pos);
467 c = w & 0xff;
469 if (w & vc->vc_hi_font_mask) {
470 w &= ~vc->vc_hi_font_mask;
471 c |= 0x100;
474 ch = inverse_translate(vc, c, 1);
475 *attribs = (w & 0xff00) >> 8;
477 return ch;
480 static void say_char(struct vc_data *vc)
482 u16 ch;
484 spk_old_attr = spk_attr;
485 ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
486 if (spk_attr != spk_old_attr) {
487 if (spk_attrib_bleep & 1)
488 bleep(spk_y);
489 if (spk_attrib_bleep & 2)
490 say_attributes(vc);
492 speak_char(ch);
495 static void say_phonetic_char(struct vc_data *vc)
497 u16 ch;
499 spk_old_attr = spk_attr;
500 ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
501 if (ch <= 0x7f && isalpha(ch)) {
502 ch &= 0x1f;
503 synth_printf("%s\n", phonetic[--ch]);
504 } else {
505 if (ch < 0x100 && IS_CHAR(ch, B_NUM))
506 synth_printf("%s ", spk_msg_get(MSG_NUMBER));
507 speak_char(ch);
511 static void say_prev_char(struct vc_data *vc)
513 spk_parked |= 0x01;
514 if (spk_x == 0) {
515 announce_edge(vc, edge_left);
516 return;
518 spk_x--;
519 spk_pos -= 2;
520 say_char(vc);
523 static void say_next_char(struct vc_data *vc)
525 spk_parked |= 0x01;
526 if (spk_x == vc->vc_cols - 1) {
527 announce_edge(vc, edge_right);
528 return;
530 spk_x++;
531 spk_pos += 2;
532 say_char(vc);
535 /* get_word - will first check to see if the character under the
536 * reading cursor is a space and if spk_say_word_ctl is true it will
537 * return the word space. If spk_say_word_ctl is not set it will check to
538 * see if there is a word starting on the next position to the right
539 * and return that word if it exists. If it does not exist it will
540 * move left to the beginning of any previous word on the line or the
541 * beginning off the line whichever comes first..
544 static u_long get_word(struct vc_data *vc)
546 u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;
547 u16 ch;
548 u16 attr_ch;
549 u_char temp;
551 spk_old_attr = spk_attr;
552 ch = get_char(vc, (u_short *)tmp_pos, &temp);
554 /* decided to take out the sayword if on a space (mis-information */
555 if (spk_say_word_ctl && ch == SPACE) {
556 *buf = '\0';
557 synth_printf("%s\n", spk_msg_get(MSG_SPACE));
558 return 0;
559 } else if (tmpx < vc->vc_cols - 2 &&
560 (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) &&
561 get_char(vc, (u_short *)&tmp_pos + 1, &temp) > SPACE) {
562 tmp_pos += 2;
563 tmpx++;
564 } else
565 while (tmpx > 0) {
566 ch = get_char(vc, (u_short *)tmp_pos - 1, &temp);
567 if ((ch == SPACE || ch == 0 ||
568 (ch < 0x100 && IS_WDLM(ch))) &&
569 get_char(vc, (u_short *)tmp_pos, &temp) > SPACE)
570 break;
571 tmp_pos -= 2;
572 tmpx--;
574 attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
575 buf[cnt++] = attr_ch;
576 while (tmpx < vc->vc_cols - 1) {
577 tmp_pos += 2;
578 tmpx++;
579 ch = get_char(vc, (u_short *)tmp_pos, &temp);
580 if (ch == SPACE || ch == 0 ||
581 (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) &&
582 ch > SPACE))
583 break;
584 buf[cnt++] = ch;
586 buf[cnt] = '\0';
587 return cnt;
590 static void say_word(struct vc_data *vc)
592 u_long cnt = get_word(vc);
593 u_short saved_punc_mask = spk_punc_mask;
595 if (cnt == 0)
596 return;
597 spk_punc_mask = PUNC;
598 buf[cnt++] = SPACE;
599 spkup_write(buf, cnt);
600 spk_punc_mask = saved_punc_mask;
603 static void say_prev_word(struct vc_data *vc)
605 u_char temp;
606 u16 ch;
607 u_short edge_said = 0, last_state = 0, state = 0;
609 spk_parked |= 0x01;
611 if (spk_x == 0) {
612 if (spk_y == 0) {
613 announce_edge(vc, edge_top);
614 return;
616 spk_y--;
617 spk_x = vc->vc_cols;
618 edge_said = edge_quiet;
620 while (1) {
621 if (spk_x == 0) {
622 if (spk_y == 0) {
623 edge_said = edge_top;
624 break;
626 if (edge_said != edge_quiet)
627 edge_said = edge_left;
628 if (state > 0)
629 break;
630 spk_y--;
631 spk_x = vc->vc_cols - 1;
632 } else {
633 spk_x--;
635 spk_pos -= 2;
636 ch = get_char(vc, (u_short *)spk_pos, &temp);
637 if (ch == SPACE || ch == 0)
638 state = 0;
639 else if (ch < 0x100 && IS_WDLM(ch))
640 state = 1;
641 else
642 state = 2;
643 if (state < last_state) {
644 spk_pos += 2;
645 spk_x++;
646 break;
648 last_state = state;
650 if (spk_x == 0 && edge_said == edge_quiet)
651 edge_said = edge_left;
652 if (edge_said > 0 && edge_said < edge_quiet)
653 announce_edge(vc, edge_said);
654 say_word(vc);
657 static void say_next_word(struct vc_data *vc)
659 u_char temp;
660 u16 ch;
661 u_short edge_said = 0, last_state = 2, state = 0;
663 spk_parked |= 0x01;
664 if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
665 announce_edge(vc, edge_bottom);
666 return;
668 while (1) {
669 ch = get_char(vc, (u_short *)spk_pos, &temp);
670 if (ch == SPACE || ch == 0)
671 state = 0;
672 else if (ch < 0x100 && IS_WDLM(ch))
673 state = 1;
674 else
675 state = 2;
676 if (state > last_state)
677 break;
678 if (spk_x >= vc->vc_cols - 1) {
679 if (spk_y == vc->vc_rows - 1) {
680 edge_said = edge_bottom;
681 break;
683 state = 0;
684 spk_y++;
685 spk_x = 0;
686 edge_said = edge_right;
687 } else {
688 spk_x++;
690 spk_pos += 2;
691 last_state = state;
693 if (edge_said > 0)
694 announce_edge(vc, edge_said);
695 say_word(vc);
698 static void spell_word(struct vc_data *vc)
700 static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
701 u16 *cp = buf;
702 char *cp1;
703 char *str_cap = spk_str_caps_stop;
704 char *last_cap = spk_str_caps_stop;
705 struct var_t *direct = spk_get_var(DIRECT);
706 u16 ch;
708 if (!get_word(vc))
709 return;
710 while ((ch = *cp)) {
711 if (cp != buf)
712 synth_printf(" %s ", delay_str[spk_spell_delay]);
713 /* FIXME: Non-latin1 considered as lower case */
714 if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
715 str_cap = spk_str_caps_start;
716 if (*spk_str_caps_stop)
717 spk_pitch_shift++;
718 else /* synth has no pitch */
719 last_cap = spk_str_caps_stop;
720 } else {
721 str_cap = spk_str_caps_stop;
723 if (str_cap != last_cap) {
724 synth_printf("%s", str_cap);
725 last_cap = str_cap;
727 if (ch >= 0x100 || (direct && direct->u.n.value)) {
728 synth_putwc_s(ch);
729 } else if (this_speakup_key == SPELL_PHONETIC &&
730 ch <= 0x7f && isalpha(ch)) {
731 ch &= 0x1f;
732 cp1 = phonetic[--ch];
733 synth_printf("%s", cp1);
734 } else {
735 cp1 = spk_characters[ch];
736 if (*cp1 == '^') {
737 synth_printf("%s", spk_msg_get(MSG_CTRL));
738 cp1++;
740 synth_printf("%s", cp1);
742 cp++;
744 if (str_cap != spk_str_caps_stop)
745 synth_printf("%s", spk_str_caps_stop);
748 static int get_line(struct vc_data *vc)
750 u_long tmp = spk_pos - (spk_x * 2);
751 int i = 0;
752 u_char tmp2;
754 spk_old_attr = spk_attr;
755 spk_attr = get_attributes(vc, (u_short *)spk_pos);
756 for (i = 0; i < vc->vc_cols; i++) {
757 buf[i] = get_char(vc, (u_short *)tmp, &tmp2);
758 tmp += 2;
760 for (--i; i >= 0; i--)
761 if (buf[i] != SPACE)
762 break;
763 return ++i;
766 static void say_line(struct vc_data *vc)
768 int i = get_line(vc);
769 u16 *cp;
770 u_short saved_punc_mask = spk_punc_mask;
772 if (i == 0) {
773 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
774 return;
776 buf[i++] = '\n';
777 if (this_speakup_key == SAY_LINE_INDENT) {
778 cp = buf;
779 while (*cp == SPACE)
780 cp++;
781 synth_printf("%zd, ", (cp - buf) + 1);
783 spk_punc_mask = spk_punc_masks[spk_reading_punc];
784 spkup_write(buf, i);
785 spk_punc_mask = saved_punc_mask;
788 static void say_prev_line(struct vc_data *vc)
790 spk_parked |= 0x01;
791 if (spk_y == 0) {
792 announce_edge(vc, edge_top);
793 return;
795 spk_y--;
796 spk_pos -= vc->vc_size_row;
797 say_line(vc);
800 static void say_next_line(struct vc_data *vc)
802 spk_parked |= 0x01;
803 if (spk_y == vc->vc_rows - 1) {
804 announce_edge(vc, edge_bottom);
805 return;
807 spk_y++;
808 spk_pos += vc->vc_size_row;
809 say_line(vc);
812 static int say_from_to(struct vc_data *vc, u_long from, u_long to,
813 int read_punc)
815 int i = 0;
816 u_char tmp;
817 u_short saved_punc_mask = spk_punc_mask;
819 spk_old_attr = spk_attr;
820 spk_attr = get_attributes(vc, (u_short *)from);
821 while (from < to) {
822 buf[i++] = get_char(vc, (u_short *)from, &tmp);
823 from += 2;
824 if (i >= vc->vc_size_row)
825 break;
827 for (--i; i >= 0; i--)
828 if (buf[i] != SPACE)
829 break;
830 buf[++i] = SPACE;
831 buf[++i] = '\0';
832 if (i < 1)
833 return i;
834 if (read_punc)
835 spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
836 spkup_write(buf, i);
837 if (read_punc)
838 spk_punc_mask = saved_punc_mask;
839 return i - 1;
842 static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
843 int read_punc)
845 u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
846 u_long end = start + (to * 2);
848 start += from * 2;
849 if (say_from_to(vc, start, end, read_punc) <= 0)
850 if (cursor_track != read_all_mode)
851 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
854 /* Sentence Reading Commands */
856 static int currsentence;
857 static int numsentences[2];
858 static u16 *sentbufend[2];
859 static u16 *sentmarks[2][10];
860 static int currbuf;
861 static int bn;
862 static u16 sentbuf[2][256];
864 static int say_sentence_num(int num, int prev)
866 bn = currbuf;
867 currsentence = num + 1;
868 if (prev && --bn == -1)
869 bn = 1;
871 if (num > numsentences[bn])
872 return 0;
874 spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
875 return 1;
878 static int get_sentence_buf(struct vc_data *vc, int read_punc)
880 u_long start, end;
881 int i, bn;
882 u_char tmp;
884 currbuf++;
885 if (currbuf == 2)
886 currbuf = 0;
887 bn = currbuf;
888 start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
889 end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
891 numsentences[bn] = 0;
892 sentmarks[bn][0] = &sentbuf[bn][0];
893 i = 0;
894 spk_old_attr = spk_attr;
895 spk_attr = get_attributes(vc, (u_short *)start);
897 while (start < end) {
898 sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);
899 if (i > 0) {
900 if (sentbuf[bn][i] == SPACE && sentbuf[bn][i - 1] == '.' &&
901 numsentences[bn] < 9) {
902 /* Sentence Marker */
903 numsentences[bn]++;
904 sentmarks[bn][numsentences[bn]] =
905 &sentbuf[bn][i];
908 i++;
909 start += 2;
910 if (i >= vc->vc_size_row)
911 break;
914 for (--i; i >= 0; i--)
915 if (sentbuf[bn][i] != SPACE)
916 break;
918 if (i < 1)
919 return -1;
921 sentbuf[bn][++i] = SPACE;
922 sentbuf[bn][++i] = '\0';
924 sentbufend[bn] = &sentbuf[bn][i];
925 return numsentences[bn];
928 static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
930 u_long start = vc->vc_origin, end;
932 if (from > 0)
933 start += from * vc->vc_size_row;
934 if (to > vc->vc_rows)
935 to = vc->vc_rows;
936 end = vc->vc_origin + (to * vc->vc_size_row);
937 for (from = start; from < end; from = to) {
938 to = from + vc->vc_size_row;
939 say_from_to(vc, from, to, 1);
943 static void say_screen(struct vc_data *vc)
945 say_screen_from_to(vc, 0, vc->vc_rows);
948 static void speakup_win_say(struct vc_data *vc)
950 u_long start, end, from, to;
952 if (win_start < 2) {
953 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
954 return;
956 start = vc->vc_origin + (win_top * vc->vc_size_row);
957 end = vc->vc_origin + (win_bottom * vc->vc_size_row);
958 while (start <= end) {
959 from = start + (win_left * 2);
960 to = start + (win_right * 2);
961 say_from_to(vc, from, to, 1);
962 start += vc->vc_size_row;
966 static void top_edge(struct vc_data *vc)
968 spk_parked |= 0x01;
969 spk_pos = vc->vc_origin + 2 * spk_x;
970 spk_y = 0;
971 say_line(vc);
974 static void bottom_edge(struct vc_data *vc)
976 spk_parked |= 0x01;
977 spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
978 spk_y = vc->vc_rows - 1;
979 say_line(vc);
982 static void left_edge(struct vc_data *vc)
984 spk_parked |= 0x01;
985 spk_pos -= spk_x * 2;
986 spk_x = 0;
987 say_char(vc);
990 static void right_edge(struct vc_data *vc)
992 spk_parked |= 0x01;
993 spk_pos += (vc->vc_cols - spk_x - 1) * 2;
994 spk_x = vc->vc_cols - 1;
995 say_char(vc);
998 static void say_first_char(struct vc_data *vc)
1000 int i, len = get_line(vc);
1001 u16 ch;
1003 spk_parked |= 0x01;
1004 if (len == 0) {
1005 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
1006 return;
1008 for (i = 0; i < len; i++)
1009 if (buf[i] != SPACE)
1010 break;
1011 ch = buf[i];
1012 spk_pos -= (spk_x - i) * 2;
1013 spk_x = i;
1014 synth_printf("%d, ", ++i);
1015 speak_char(ch);
1018 static void say_last_char(struct vc_data *vc)
1020 int len = get_line(vc);
1021 u16 ch;
1023 spk_parked |= 0x01;
1024 if (len == 0) {
1025 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
1026 return;
1028 ch = buf[--len];
1029 spk_pos -= (spk_x - len) * 2;
1030 spk_x = len;
1031 synth_printf("%d, ", ++len);
1032 speak_char(ch);
1035 static void say_position(struct vc_data *vc)
1037 synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
1038 vc->vc_num + 1);
1039 synth_printf("\n");
1042 /* Added by brianb */
1043 static void say_char_num(struct vc_data *vc)
1045 u_char tmp;
1046 u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);
1048 synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
1051 /* these are stub functions to keep keyboard.c happy. */
1053 static void say_from_top(struct vc_data *vc)
1055 say_screen_from_to(vc, 0, spk_y);
1058 static void say_to_bottom(struct vc_data *vc)
1060 say_screen_from_to(vc, spk_y, vc->vc_rows);
1063 static void say_from_left(struct vc_data *vc)
1065 say_line_from_to(vc, 0, spk_x, 1);
1068 static void say_to_right(struct vc_data *vc)
1070 say_line_from_to(vc, spk_x, vc->vc_cols, 1);
1073 /* end of stub functions. */
1075 static void spkup_write(const u16 *in_buf, int count)
1077 static int rep_count;
1078 static u16 ch = '\0', old_ch = '\0';
1079 static u_short char_type, last_type;
1080 int in_count = count;
1082 spk_keydown = 0;
1083 while (count--) {
1084 if (cursor_track == read_all_mode) {
1085 /* Insert Sentence Index */
1086 if ((in_buf == sentmarks[bn][currsentence]) &&
1087 (currsentence <= numsentences[bn]))
1088 synth_insert_next_index(currsentence++);
1090 ch = *in_buf++;
1091 if (ch < 0x100)
1092 char_type = spk_chartab[ch];
1093 else
1094 char_type = ALPHA;
1095 if (ch == old_ch && !(char_type & B_NUM)) {
1096 if (++rep_count > 2)
1097 continue;
1098 } else {
1099 if ((last_type & CH_RPT) && rep_count > 2) {
1100 synth_printf(" ");
1101 synth_printf(spk_msg_get(MSG_REPEAT_DESC),
1102 ++rep_count);
1103 synth_printf(" ");
1105 rep_count = 0;
1107 if (ch == spk_lastkey) {
1108 rep_count = 0;
1109 if (spk_key_echo == 1 && ch >= MINECHOCHAR)
1110 speak_char(ch);
1111 } else if (char_type & B_ALPHA) {
1112 if ((synth_flags & SF_DEC) && (last_type & PUNC))
1113 synth_buffer_add(SPACE);
1114 synth_putwc_s(ch);
1115 } else if (char_type & B_NUM) {
1116 rep_count = 0;
1117 synth_putwc_s(ch);
1118 } else if (char_type & spk_punc_mask) {
1119 speak_char(ch);
1120 char_type &= ~PUNC; /* for dec nospell processing */
1121 } else if (char_type & SYNTH_OK) {
1122 /* these are usually puncts like . and , which synth
1123 * needs for expression.
1124 * suppress multiple to get rid of long pauses and
1125 * clear repeat count
1126 * so if someone has
1127 * repeats on you don't get nothing repeated count
1129 if (ch != old_ch)
1130 synth_putwc_s(ch);
1131 else
1132 rep_count = 0;
1133 } else {
1134 /* send space and record position, if next is num overwrite space */
1135 if (old_ch != ch)
1136 synth_buffer_add(SPACE);
1137 else
1138 rep_count = 0;
1140 old_ch = ch;
1141 last_type = char_type;
1143 spk_lastkey = 0;
1144 if (in_count > 2 && rep_count > 2) {
1145 if (last_type & CH_RPT) {
1146 synth_printf(" ");
1147 synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
1148 ++rep_count);
1149 synth_printf(" ");
1151 rep_count = 0;
1155 static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
1157 static void read_all_doc(struct vc_data *vc);
1158 static void cursor_done(struct timer_list *unused);
1159 static DEFINE_TIMER(cursor_timer, cursor_done);
1161 static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
1163 unsigned long flags;
1165 if (!synth || up_flag || spk_killed)
1166 return;
1167 spin_lock_irqsave(&speakup_info.spinlock, flags);
1168 if (cursor_track == read_all_mode) {
1169 switch (value) {
1170 case KVAL(K_SHIFT):
1171 del_timer(&cursor_timer);
1172 spk_shut_up &= 0xfe;
1173 spk_do_flush();
1174 read_all_doc(vc);
1175 break;
1176 case KVAL(K_CTRL):
1177 del_timer(&cursor_timer);
1178 cursor_track = prev_cursor_track;
1179 spk_shut_up &= 0xfe;
1180 spk_do_flush();
1181 break;
1183 } else {
1184 spk_shut_up &= 0xfe;
1185 spk_do_flush();
1187 if (spk_say_ctrl && value < NUM_CTL_LABELS)
1188 synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
1189 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1192 static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
1194 unsigned long flags;
1196 spin_lock_irqsave(&speakup_info.spinlock, flags);
1197 if (up_flag) {
1198 spk_lastkey = 0;
1199 spk_keydown = 0;
1200 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1201 return;
1203 if (!synth || spk_killed) {
1204 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1205 return;
1207 spk_shut_up &= 0xfe;
1208 spk_lastkey = value;
1209 spk_keydown++;
1210 spk_parked &= 0xfe;
1211 if (spk_key_echo == 2 && value >= MINECHOCHAR)
1212 speak_char(value);
1213 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1216 int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
1218 int i = 0, states, key_data_len;
1219 const u_char *cp = key_info;
1220 u_char *cp1 = k_buffer;
1221 u_char ch, version, num_keys;
1223 version = *cp++;
1224 if (version != KEY_MAP_VER) {
1225 pr_debug("version found %d should be %d\n",
1226 version, KEY_MAP_VER);
1227 return -EINVAL;
1229 num_keys = *cp;
1230 states = (int)cp[1];
1231 key_data_len = (states + 1) * (num_keys + 1);
1232 if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
1233 pr_debug("too many key_infos (%d over %u)\n",
1234 key_data_len + SHIFT_TBL_SIZE + 4, (unsigned int)(sizeof(spk_key_buf)));
1235 return -EINVAL;
1237 memset(k_buffer, 0, SHIFT_TBL_SIZE);
1238 memset(spk_our_keys, 0, sizeof(spk_our_keys));
1239 spk_shift_table = k_buffer;
1240 spk_our_keys[0] = spk_shift_table;
1241 cp1 += SHIFT_TBL_SIZE;
1242 memcpy(cp1, cp, key_data_len + 3);
1243 /* get num_keys, states and data */
1244 cp1 += 2; /* now pointing at shift states */
1245 for (i = 1; i <= states; i++) {
1246 ch = *cp1++;
1247 if (ch >= SHIFT_TBL_SIZE) {
1248 pr_debug("(%d) not valid shift state (max_allowed = %d)\n", ch,
1249 SHIFT_TBL_SIZE);
1250 return -EINVAL;
1252 spk_shift_table[ch] = i;
1254 keymap_flags = *cp1++;
1255 while ((ch = *cp1)) {
1256 if (ch >= MAX_KEY) {
1257 pr_debug("(%d), not valid key, (max_allowed = %d)\n", ch, MAX_KEY);
1258 return -EINVAL;
1260 spk_our_keys[ch] = cp1;
1261 cp1 += states + 1;
1263 return 0;
1266 static struct var_t spk_vars[] = {
1267 /* bell must be first to set high limit */
1268 {BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
1269 {SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
1270 {ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
1271 {BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
1272 {BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
1273 {PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1274 {READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1275 {CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
1276 {SAY_CONTROL, TOGGLE_0},
1277 {SAY_WORD_CTL, TOGGLE_0},
1278 {NO_INTERRUPT, TOGGLE_0},
1279 {KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
1280 V_LAST_VAR
1283 static void toggle_cursoring(struct vc_data *vc)
1285 if (cursor_track == read_all_mode)
1286 cursor_track = prev_cursor_track;
1287 if (++cursor_track >= CT_Max)
1288 cursor_track = 0;
1289 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
1292 void spk_reset_default_chars(void)
1294 int i;
1296 /* First, free any non-default */
1297 for (i = 0; i < 256; i++) {
1298 if (spk_characters[i] &&
1299 (spk_characters[i] != spk_default_chars[i]))
1300 kfree(spk_characters[i]);
1303 memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
1306 void spk_reset_default_chartab(void)
1308 memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
1311 static const struct st_bits_data *pb_edit;
1313 static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
1315 short mask = pb_edit->mask, ch_type = spk_chartab[ch];
1317 if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
1318 return -1;
1319 if (ch == SPACE) {
1320 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
1321 spk_special_handler = NULL;
1322 return 1;
1324 if (mask < PUNC && !(ch_type & PUNC))
1325 return -1;
1326 spk_chartab[ch] ^= mask;
1327 speak_char(ch);
1328 synth_printf(" %s\n",
1329 (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
1330 spk_msg_get(MSG_OFF));
1331 return 1;
1334 /* Allocation concurrency is protected by the console semaphore */
1335 static int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags)
1337 int vc_num;
1339 vc_num = vc->vc_num;
1340 if (!speakup_console[vc_num]) {
1341 speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
1342 gfp_flags);
1343 if (!speakup_console[vc_num])
1344 return -ENOMEM;
1345 speakup_date(vc);
1346 } else if (!spk_parked) {
1347 speakup_date(vc);
1350 return 0;
1353 static void speakup_deallocate(struct vc_data *vc)
1355 int vc_num;
1357 vc_num = vc->vc_num;
1358 kfree(speakup_console[vc_num]);
1359 speakup_console[vc_num] = NULL;
1362 static u_char is_cursor;
1363 static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
1364 static int cursor_con;
1366 static void reset_highlight_buffers(struct vc_data *);
1368 static int read_all_key;
1370 static int in_keyboard_notifier;
1372 static void start_read_all_timer(struct vc_data *vc, int command);
1374 enum {
1375 RA_NOTHING,
1376 RA_NEXT_SENT,
1377 RA_PREV_LINE,
1378 RA_NEXT_LINE,
1379 RA_PREV_SENT,
1380 RA_DOWN_ARROW,
1381 RA_TIMER,
1382 RA_FIND_NEXT_SENT,
1383 RA_FIND_PREV_SENT,
1386 static void kbd_fakekey2(struct vc_data *vc, int command)
1388 del_timer(&cursor_timer);
1389 speakup_fake_down_arrow();
1390 start_read_all_timer(vc, command);
1393 static void read_all_doc(struct vc_data *vc)
1395 if ((vc->vc_num != fg_console) || !synth || spk_shut_up)
1396 return;
1397 if (!synth_supports_indexing())
1398 return;
1399 if (cursor_track != read_all_mode)
1400 prev_cursor_track = cursor_track;
1401 cursor_track = read_all_mode;
1402 spk_reset_index_count(0);
1403 if (get_sentence_buf(vc, 0) == -1) {
1404 del_timer(&cursor_timer);
1405 if (!in_keyboard_notifier)
1406 speakup_fake_down_arrow();
1407 start_read_all_timer(vc, RA_DOWN_ARROW);
1408 } else {
1409 say_sentence_num(0, 0);
1410 synth_insert_next_index(0);
1411 start_read_all_timer(vc, RA_TIMER);
1415 static void stop_read_all(struct vc_data *vc)
1417 del_timer(&cursor_timer);
1418 cursor_track = prev_cursor_track;
1419 spk_shut_up &= 0xfe;
1420 spk_do_flush();
1423 static void start_read_all_timer(struct vc_data *vc, int command)
1425 struct var_t *cursor_timeout;
1427 cursor_con = vc->vc_num;
1428 read_all_key = command;
1429 cursor_timeout = spk_get_var(CURSOR_TIME);
1430 mod_timer(&cursor_timer,
1431 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
1434 static void handle_cursor_read_all(struct vc_data *vc, int command)
1436 int indcount, sentcount, rv, sn;
1438 switch (command) {
1439 case RA_NEXT_SENT:
1440 /* Get Current Sentence */
1441 spk_get_index_count(&indcount, &sentcount);
1442 /*printk("%d %d ", indcount, sentcount); */
1443 spk_reset_index_count(sentcount + 1);
1444 if (indcount == 1) {
1445 if (!say_sentence_num(sentcount + 1, 0)) {
1446 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1447 return;
1449 synth_insert_next_index(0);
1450 } else {
1451 sn = 0;
1452 if (!say_sentence_num(sentcount + 1, 1)) {
1453 sn = 1;
1454 spk_reset_index_count(sn);
1455 } else {
1456 synth_insert_next_index(0);
1458 if (!say_sentence_num(sn, 0)) {
1459 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1460 return;
1462 synth_insert_next_index(0);
1464 start_read_all_timer(vc, RA_TIMER);
1465 break;
1466 case RA_PREV_SENT:
1467 break;
1468 case RA_NEXT_LINE:
1469 read_all_doc(vc);
1470 break;
1471 case RA_PREV_LINE:
1472 break;
1473 case RA_DOWN_ARROW:
1474 if (get_sentence_buf(vc, 0) == -1) {
1475 kbd_fakekey2(vc, RA_DOWN_ARROW);
1476 } else {
1477 say_sentence_num(0, 0);
1478 synth_insert_next_index(0);
1479 start_read_all_timer(vc, RA_TIMER);
1481 break;
1482 case RA_FIND_NEXT_SENT:
1483 rv = get_sentence_buf(vc, 0);
1484 if (rv == -1)
1485 read_all_doc(vc);
1486 if (rv == 0) {
1487 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1488 } else {
1489 say_sentence_num(1, 0);
1490 synth_insert_next_index(0);
1491 start_read_all_timer(vc, RA_TIMER);
1493 break;
1494 case RA_FIND_PREV_SENT:
1495 break;
1496 case RA_TIMER:
1497 spk_get_index_count(&indcount, &sentcount);
1498 if (indcount < 2)
1499 kbd_fakekey2(vc, RA_DOWN_ARROW);
1500 else
1501 start_read_all_timer(vc, RA_TIMER);
1502 break;
1506 static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1508 unsigned long flags;
1510 spin_lock_irqsave(&speakup_info.spinlock, flags);
1511 if (cursor_track == read_all_mode) {
1512 spk_parked &= 0xfe;
1513 if (!synth || up_flag || spk_shut_up) {
1514 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1515 return NOTIFY_STOP;
1517 del_timer(&cursor_timer);
1518 spk_shut_up &= 0xfe;
1519 spk_do_flush();
1520 start_read_all_timer(vc, value + 1);
1521 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1522 return NOTIFY_STOP;
1524 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1525 return NOTIFY_OK;
1528 static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1530 unsigned long flags;
1531 struct var_t *cursor_timeout;
1533 spin_lock_irqsave(&speakup_info.spinlock, flags);
1534 spk_parked &= 0xfe;
1535 if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) {
1536 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1537 return;
1539 spk_shut_up &= 0xfe;
1540 if (spk_no_intr)
1541 spk_do_flush();
1542 /* the key press flushes if !no_inter but we want to flush on cursor
1543 * moves regardless of no_inter state
1545 is_cursor = value + 1;
1546 old_cursor_pos = vc->vc_pos;
1547 old_cursor_x = vc->vc_x;
1548 old_cursor_y = vc->vc_y;
1549 speakup_console[vc->vc_num]->ht.cy = vc->vc_y;
1550 cursor_con = vc->vc_num;
1551 if (cursor_track == CT_Highlight)
1552 reset_highlight_buffers(vc);
1553 cursor_timeout = spk_get_var(CURSOR_TIME);
1554 mod_timer(&cursor_timer,
1555 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
1556 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1559 static void update_color_buffer(struct vc_data *vc, const u16 *ic, int len)
1561 int i, bi, hi;
1562 int vc_num = vc->vc_num;
1564 bi = (vc->vc_attr & 0x70) >> 4;
1565 hi = speakup_console[vc_num]->ht.highsize[bi];
1567 i = 0;
1568 if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
1569 speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
1570 speakup_console[vc_num]->ht.rx[bi] = vc->vc_x;
1571 speakup_console[vc_num]->ht.ry[bi] = vc->vc_y;
1573 while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
1574 if (ic[i] > 32) {
1575 speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
1576 hi++;
1577 } else if ((ic[i] == 32) && (hi != 0)) {
1578 if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
1579 32) {
1580 speakup_console[vc_num]->ht.highbuf[bi][hi] =
1581 ic[i];
1582 hi++;
1585 i++;
1587 speakup_console[vc_num]->ht.highsize[bi] = hi;
1590 static void reset_highlight_buffers(struct vc_data *vc)
1592 int i;
1593 int vc_num = vc->vc_num;
1595 for (i = 0; i < 8; i++)
1596 speakup_console[vc_num]->ht.highsize[i] = 0;
1599 static int count_highlight_color(struct vc_data *vc)
1601 int i, bg;
1602 int cc;
1603 int vc_num = vc->vc_num;
1604 u16 ch;
1605 u16 *start = (u16 *)vc->vc_origin;
1607 for (i = 0; i < 8; i++)
1608 speakup_console[vc_num]->ht.bgcount[i] = 0;
1610 for (i = 0; i < vc->vc_rows; i++) {
1611 u16 *end = start + vc->vc_cols * 2;
1612 u16 *ptr;
1614 for (ptr = start; ptr < end; ptr++) {
1615 ch = get_attributes(vc, ptr);
1616 bg = (ch & 0x70) >> 4;
1617 speakup_console[vc_num]->ht.bgcount[bg]++;
1619 start += vc->vc_size_row;
1622 cc = 0;
1623 for (i = 0; i < 8; i++)
1624 if (speakup_console[vc_num]->ht.bgcount[i] > 0)
1625 cc++;
1626 return cc;
1629 static int get_highlight_color(struct vc_data *vc)
1631 int i, j;
1632 unsigned int cptr[8];
1633 int vc_num = vc->vc_num;
1635 for (i = 0; i < 8; i++)
1636 cptr[i] = i;
1638 for (i = 0; i < 7; i++)
1639 for (j = i + 1; j < 8; j++)
1640 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
1641 speakup_console[vc_num]->ht.bgcount[cptr[j]])
1642 swap(cptr[i], cptr[j]);
1644 for (i = 0; i < 8; i++)
1645 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
1646 if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
1647 return cptr[i];
1648 return -1;
1651 static int speak_highlight(struct vc_data *vc)
1653 int hc, d;
1654 int vc_num = vc->vc_num;
1656 if (count_highlight_color(vc) == 1)
1657 return 0;
1658 hc = get_highlight_color(vc);
1659 if (hc != -1) {
1660 d = vc->vc_y - speakup_console[vc_num]->ht.cy;
1661 if ((d == 1) || (d == -1))
1662 if (speakup_console[vc_num]->ht.ry[hc] != vc->vc_y)
1663 return 0;
1664 spk_parked |= 0x01;
1665 spk_do_flush();
1666 spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
1667 speakup_console[vc_num]->ht.highsize[hc]);
1668 spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
1669 spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
1670 spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
1671 return 1;
1673 return 0;
1676 static void cursor_done(struct timer_list *unused)
1678 struct vc_data *vc = vc_cons[cursor_con].d;
1679 unsigned long flags;
1681 del_timer(&cursor_timer);
1682 spin_lock_irqsave(&speakup_info.spinlock, flags);
1683 if (cursor_con != fg_console) {
1684 is_cursor = 0;
1685 goto out;
1687 speakup_date(vc);
1688 if (win_enabled) {
1689 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
1690 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
1691 spk_keydown = 0;
1692 is_cursor = 0;
1693 goto out;
1696 if (cursor_track == read_all_mode) {
1697 handle_cursor_read_all(vc, read_all_key);
1698 goto out;
1700 if (cursor_track == CT_Highlight) {
1701 if (speak_highlight(vc)) {
1702 spk_keydown = 0;
1703 is_cursor = 0;
1704 goto out;
1707 if (cursor_track == CT_Window)
1708 speakup_win_say(vc);
1709 else if (is_cursor == 1 || is_cursor == 4)
1710 say_line_from_to(vc, 0, vc->vc_cols, 0);
1711 else
1712 say_char(vc);
1713 spk_keydown = 0;
1714 is_cursor = 0;
1715 out:
1716 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1719 /* called by: vt_notifier_call() */
1720 static void speakup_bs(struct vc_data *vc)
1722 unsigned long flags;
1724 if (!speakup_console[vc->vc_num])
1725 return;
1726 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1727 /* Speakup output, discard */
1728 return;
1729 if (!spk_parked)
1730 speakup_date(vc);
1731 if (spk_shut_up || !synth) {
1732 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1733 return;
1735 if (vc->vc_num == fg_console && spk_keydown) {
1736 spk_keydown = 0;
1737 if (!is_cursor)
1738 say_char(vc);
1740 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1743 /* called by: vt_notifier_call() */
1744 static void speakup_con_write(struct vc_data *vc, u16 *str, int len)
1746 unsigned long flags;
1748 if ((vc->vc_num != fg_console) || spk_shut_up || !synth)
1749 return;
1750 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1751 /* Speakup output, discard */
1752 return;
1753 if (spk_bell_pos && spk_keydown && (vc->vc_x == spk_bell_pos - 1))
1754 bleep(3);
1755 if ((is_cursor) || (cursor_track == read_all_mode)) {
1756 if (cursor_track == CT_Highlight)
1757 update_color_buffer(vc, str, len);
1758 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1759 return;
1761 if (win_enabled) {
1762 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
1763 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
1764 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1765 return;
1769 spkup_write(str, len);
1770 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1773 static void speakup_con_update(struct vc_data *vc)
1775 unsigned long flags;
1777 if (!speakup_console[vc->vc_num] || spk_parked)
1778 return;
1779 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1780 /* Speakup output, discard */
1781 return;
1782 speakup_date(vc);
1783 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1786 static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
1788 unsigned long flags;
1789 int on_off = 2;
1790 char *label;
1792 if (!synth || up_flag || spk_killed)
1793 return;
1794 spin_lock_irqsave(&speakup_info.spinlock, flags);
1795 spk_shut_up &= 0xfe;
1796 if (spk_no_intr)
1797 spk_do_flush();
1798 switch (value) {
1799 case KVAL(K_CAPS):
1800 label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
1801 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
1802 break;
1803 case KVAL(K_NUM):
1804 label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
1805 on_off = vt_get_leds(fg_console, VC_NUMLOCK);
1806 break;
1807 case KVAL(K_HOLD):
1808 label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
1809 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
1810 if (speakup_console[vc->vc_num])
1811 speakup_console[vc->vc_num]->tty_stopped = on_off;
1812 break;
1813 default:
1814 spk_parked &= 0xfe;
1815 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1816 return;
1818 if (on_off < 2)
1819 synth_printf("%s %s\n",
1820 label, spk_msg_get(MSG_STATUS_START + on_off));
1821 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1824 static int inc_dec_var(u_char value)
1826 struct st_var_header *p_header;
1827 struct var_t *var_data;
1828 char num_buf[32];
1829 char *cp = num_buf;
1830 char *pn;
1831 int var_id = (int)value - VAR_START;
1832 int how = (var_id & 1) ? E_INC : E_DEC;
1834 var_id = var_id / 2 + FIRST_SET_VAR;
1835 p_header = spk_get_var_header(var_id);
1836 if (!p_header)
1837 return -1;
1838 if (p_header->var_type != VAR_NUM)
1839 return -1;
1840 var_data = p_header->data;
1841 if (spk_set_num_var(1, p_header, how) != 0)
1842 return -1;
1843 if (!spk_close_press) {
1844 for (pn = p_header->name; *pn; pn++) {
1845 if (*pn == '_')
1846 *cp = SPACE;
1847 else
1848 *cp++ = *pn;
1851 snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
1852 var_data->u.n.value);
1853 synth_printf("%s", num_buf);
1854 return 0;
1857 static void speakup_win_set(struct vc_data *vc)
1859 char info[40];
1861 if (win_start > 1) {
1862 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
1863 return;
1865 if (spk_x < win_left || spk_y < win_top) {
1866 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
1867 return;
1869 if (win_start && spk_x == win_left && spk_y == win_top) {
1870 win_left = 0;
1871 win_right = vc->vc_cols - 1;
1872 win_bottom = spk_y;
1873 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
1874 (int)win_top + 1);
1875 } else {
1876 if (!win_start) {
1877 win_top = spk_y;
1878 win_left = spk_x;
1879 } else {
1880 win_bottom = spk_y;
1881 win_right = spk_x;
1883 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
1884 (win_start) ?
1885 spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
1886 (int)spk_y + 1, (int)spk_x + 1);
1888 synth_printf("%s\n", info);
1889 win_start++;
1892 static void speakup_win_clear(struct vc_data *vc)
1894 win_top = 0;
1895 win_bottom = 0;
1896 win_left = 0;
1897 win_right = 0;
1898 win_start = 0;
1899 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
1902 static void speakup_win_enable(struct vc_data *vc)
1904 if (win_start < 2) {
1905 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
1906 return;
1908 win_enabled ^= 1;
1909 if (win_enabled)
1910 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
1911 else
1912 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
1915 static void speakup_bits(struct vc_data *vc)
1917 int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
1919 if (spk_special_handler || val < 1 || val > 6) {
1920 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
1921 return;
1923 pb_edit = &spk_punc_info[val];
1924 synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
1925 spk_special_handler = edit_bits;
1928 static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
1930 static u_char goto_buf[8];
1931 static int num;
1932 int maxlen;
1933 char *cp;
1934 u16 wch;
1936 if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
1937 goto do_goto;
1938 if (type == KT_LATIN && ch == '\n')
1939 goto do_goto;
1940 if (type != 0)
1941 goto oops;
1942 if (ch == 8) {
1943 u16 wch;
1945 if (num == 0)
1946 return -1;
1947 wch = goto_buf[--num];
1948 goto_buf[num] = '\0';
1949 spkup_write(&wch, 1);
1950 return 1;
1952 if (ch < '+' || ch > 'y')
1953 goto oops;
1954 wch = ch;
1955 goto_buf[num++] = ch;
1956 goto_buf[num] = '\0';
1957 spkup_write(&wch, 1);
1958 maxlen = (*goto_buf >= '0') ? 3 : 4;
1959 if ((ch == '+' || ch == '-') && num == 1)
1960 return 1;
1961 if (ch >= '0' && ch <= '9' && num < maxlen)
1962 return 1;
1963 if (num < maxlen - 1 || num > maxlen)
1964 goto oops;
1965 if (ch < 'x' || ch > 'y') {
1966 oops:
1967 if (!spk_killed)
1968 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
1969 goto_buf[num = 0] = '\0';
1970 spk_special_handler = NULL;
1971 return 1;
1974 goto_pos = simple_strtoul(goto_buf, &cp, 10);
1976 if (*cp == 'x') {
1977 if (*goto_buf < '0')
1978 goto_pos += spk_x;
1979 else if (goto_pos > 0)
1980 goto_pos--;
1982 if (goto_pos >= vc->vc_cols)
1983 goto_pos = vc->vc_cols - 1;
1984 goto_x = 1;
1985 } else {
1986 if (*goto_buf < '0')
1987 goto_pos += spk_y;
1988 else if (goto_pos > 0)
1989 goto_pos--;
1991 if (goto_pos >= vc->vc_rows)
1992 goto_pos = vc->vc_rows - 1;
1993 goto_x = 0;
1995 goto_buf[num = 0] = '\0';
1996 do_goto:
1997 spk_special_handler = NULL;
1998 spk_parked |= 0x01;
1999 if (goto_x) {
2000 spk_pos -= spk_x * 2;
2001 spk_x = goto_pos;
2002 spk_pos += goto_pos * 2;
2003 say_word(vc);
2004 } else {
2005 spk_y = goto_pos;
2006 spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
2007 say_line(vc);
2009 return 1;
2012 static void speakup_goto(struct vc_data *vc)
2014 if (spk_special_handler) {
2015 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
2016 return;
2018 synth_printf("%s\n", spk_msg_get(MSG_GOTO));
2019 spk_special_handler = handle_goto;
2022 static void speakup_help(struct vc_data *vc)
2024 spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
2027 static void do_nothing(struct vc_data *vc)
2029 return; /* flush done in do_spkup */
2032 static u_char key_speakup, spk_key_locked;
2034 static void speakup_lock(struct vc_data *vc)
2036 if (!spk_key_locked) {
2037 spk_key_locked = 16;
2038 key_speakup = 16;
2039 } else {
2040 spk_key_locked = 0;
2041 key_speakup = 0;
2045 typedef void (*spkup_hand) (struct vc_data *);
2046 static spkup_hand spkup_handler[] = {
2047 /* must be ordered same as defines in speakup.h */
2048 do_nothing, speakup_goto, speech_kill, speakup_shut_up,
2049 speakup_cut, speakup_paste, say_first_char, say_last_char,
2050 say_char, say_prev_char, say_next_char,
2051 say_word, say_prev_word, say_next_word,
2052 say_line, say_prev_line, say_next_line,
2053 top_edge, bottom_edge, left_edge, right_edge,
2054 spell_word, spell_word, say_screen,
2055 say_position, say_attributes,
2056 speakup_off, speakup_parked, say_line, /* this is for indent */
2057 say_from_top, say_to_bottom,
2058 say_from_left, say_to_right,
2059 say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
2060 speakup_bits, speakup_bits, speakup_bits,
2061 speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
2062 speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
2065 static void do_spkup(struct vc_data *vc, u_char value)
2067 if (spk_killed && value != SPEECH_KILL)
2068 return;
2069 spk_keydown = 0;
2070 spk_lastkey = 0;
2071 spk_shut_up &= 0xfe;
2072 this_speakup_key = value;
2073 if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
2074 spk_do_flush();
2075 (*spkup_handler[value]) (vc);
2076 } else {
2077 if (inc_dec_var(value) < 0)
2078 bleep(9);
2082 static const char *pad_chars = "0123456789+-*/\015,.?()";
2084 static int
2085 speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
2086 int up_flag)
2088 unsigned long flags;
2089 int kh;
2090 u_char *key_info;
2091 u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
2092 u_char shift_info, offset;
2093 int ret = 0;
2095 if (!synth)
2096 return 0;
2098 spin_lock_irqsave(&speakup_info.spinlock, flags);
2099 tty = vc->port.tty;
2100 if (type >= 0xf0)
2101 type -= 0xf0;
2102 if (type == KT_PAD &&
2103 (vt_get_leds(fg_console, VC_NUMLOCK))) {
2104 if (up_flag) {
2105 spk_keydown = 0;
2106 goto out;
2108 value = spk_lastkey = pad_chars[value];
2109 spk_keydown++;
2110 spk_parked &= 0xfe;
2111 goto no_map;
2113 if (keycode >= MAX_KEY)
2114 goto no_map;
2115 key_info = spk_our_keys[keycode];
2116 if (!key_info)
2117 goto no_map;
2118 /* Check valid read all mode keys */
2119 if ((cursor_track == read_all_mode) && (!up_flag)) {
2120 switch (value) {
2121 case KVAL(K_DOWN):
2122 case KVAL(K_UP):
2123 case KVAL(K_LEFT):
2124 case KVAL(K_RIGHT):
2125 case KVAL(K_PGUP):
2126 case KVAL(K_PGDN):
2127 break;
2128 default:
2129 stop_read_all(vc);
2130 break;
2133 shift_info = (shift_state & 0x0f) + key_speakup;
2134 offset = spk_shift_table[shift_info];
2135 if (offset) {
2136 new_key = key_info[offset];
2137 if (new_key) {
2138 ret = 1;
2139 if (new_key == SPK_KEY) {
2140 if (!spk_key_locked)
2141 key_speakup = (up_flag) ? 0 : 16;
2142 if (up_flag || spk_killed)
2143 goto out;
2144 spk_shut_up &= 0xfe;
2145 spk_do_flush();
2146 goto out;
2148 if (up_flag)
2149 goto out;
2150 if (last_keycode == keycode &&
2151 time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
2152 spk_close_press = 1;
2153 offset = spk_shift_table[shift_info + 32];
2154 /* double press? */
2155 if (offset && key_info[offset])
2156 new_key = key_info[offset];
2158 last_keycode = keycode;
2159 last_spk_jiffy = jiffies;
2160 type = KT_SPKUP;
2161 value = new_key;
2164 no_map:
2165 if (type == KT_SPKUP && !spk_special_handler) {
2166 do_spkup(vc, new_key);
2167 spk_close_press = 0;
2168 ret = 1;
2169 goto out;
2171 if (up_flag || spk_killed || type == KT_SHIFT)
2172 goto out;
2173 spk_shut_up &= 0xfe;
2174 kh = (value == KVAL(K_DOWN)) ||
2175 (value == KVAL(K_UP)) ||
2176 (value == KVAL(K_LEFT)) ||
2177 (value == KVAL(K_RIGHT));
2178 if ((cursor_track != read_all_mode) || !kh)
2179 if (!spk_no_intr)
2180 spk_do_flush();
2181 if (spk_special_handler) {
2182 if (type == KT_SPEC && value == 1) {
2183 value = '\n';
2184 type = KT_LATIN;
2185 } else if (type == KT_LETTER) {
2186 type = KT_LATIN;
2187 } else if (value == 0x7f) {
2188 value = 8; /* make del = backspace */
2190 ret = (*spk_special_handler) (vc, type, value, keycode);
2191 spk_close_press = 0;
2192 if (ret < 0)
2193 bleep(9);
2194 goto out;
2196 last_keycode = 0;
2197 out:
2198 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2199 return ret;
2202 static int keyboard_notifier_call(struct notifier_block *nb,
2203 unsigned long code, void *_param)
2205 struct keyboard_notifier_param *param = _param;
2206 struct vc_data *vc = param->vc;
2207 int up = !param->down;
2208 int ret = NOTIFY_OK;
2209 static int keycode; /* to hold the current keycode */
2211 in_keyboard_notifier = 1;
2213 if (vc->vc_mode == KD_GRAPHICS)
2214 goto out;
2217 * First, determine whether we are handling a fake keypress on
2218 * the current processor. If we are, then return NOTIFY_OK,
2219 * to pass the keystroke up the chain. This prevents us from
2220 * trying to take the Speakup lock while it is held by the
2221 * processor on which the simulated keystroke was generated.
2222 * Also, the simulated keystrokes should be ignored by Speakup.
2225 if (speakup_fake_key_pressed())
2226 goto out;
2228 switch (code) {
2229 case KBD_KEYCODE:
2230 /* speakup requires keycode and keysym currently */
2231 keycode = param->value;
2232 break;
2233 case KBD_UNBOUND_KEYCODE:
2234 /* not used yet */
2235 break;
2236 case KBD_UNICODE:
2237 /* not used yet */
2238 break;
2239 case KBD_KEYSYM:
2240 if (speakup_key(vc, param->shift, keycode, param->value, up))
2241 ret = NOTIFY_STOP;
2242 else if (KTYP(param->value) == KT_CUR)
2243 ret = pre_handle_cursor(vc, KVAL(param->value), up);
2244 break;
2245 case KBD_POST_KEYSYM:{
2246 unsigned char type = KTYP(param->value) - 0xf0;
2247 unsigned char val = KVAL(param->value);
2249 switch (type) {
2250 case KT_SHIFT:
2251 do_handle_shift(vc, val, up);
2252 break;
2253 case KT_LATIN:
2254 case KT_LETTER:
2255 do_handle_latin(vc, val, up);
2256 break;
2257 case KT_CUR:
2258 do_handle_cursor(vc, val, up);
2259 break;
2260 case KT_SPEC:
2261 do_handle_spec(vc, val, up);
2262 break;
2264 break;
2267 out:
2268 in_keyboard_notifier = 0;
2269 return ret;
2272 static int vt_notifier_call(struct notifier_block *nb,
2273 unsigned long code, void *_param)
2275 struct vt_notifier_param *param = _param;
2276 struct vc_data *vc = param->vc;
2278 switch (code) {
2279 case VT_ALLOCATE:
2280 if (vc->vc_mode == KD_TEXT)
2281 speakup_allocate(vc, GFP_ATOMIC);
2282 break;
2283 case VT_DEALLOCATE:
2284 speakup_deallocate(vc);
2285 break;
2286 case VT_WRITE:
2287 if (param->c == '\b') {
2288 speakup_bs(vc);
2289 } else {
2290 u16 d = param->c;
2292 speakup_con_write(vc, &d, 1);
2294 break;
2295 case VT_UPDATE:
2296 speakup_con_update(vc);
2297 break;
2299 return NOTIFY_OK;
2302 /* called by: module_exit() */
2303 static void __exit speakup_exit(void)
2305 int i;
2307 unregister_keyboard_notifier(&keyboard_notifier_block);
2308 unregister_vt_notifier(&vt_notifier_block);
2309 speakup_unregister_devsynth();
2310 speakup_cancel_paste();
2311 del_timer_sync(&cursor_timer);
2312 kthread_stop(speakup_task);
2313 speakup_task = NULL;
2314 mutex_lock(&spk_mutex);
2315 synth_release();
2316 mutex_unlock(&spk_mutex);
2317 spk_ttyio_unregister_ldisc();
2319 speakup_kobj_exit();
2321 for (i = 0; i < MAX_NR_CONSOLES; i++)
2322 kfree(speakup_console[i]);
2324 speakup_remove_virtual_keyboard();
2326 for (i = 0; i < MAXVARS; i++)
2327 speakup_unregister_var(i);
2329 for (i = 0; i < 256; i++) {
2330 if (spk_characters[i] != spk_default_chars[i])
2331 kfree(spk_characters[i]);
2334 spk_free_user_msgs();
2337 /* call by: module_init() */
2338 static int __init speakup_init(void)
2340 int i;
2341 long err = 0;
2342 struct vc_data *vc = vc_cons[fg_console].d;
2343 struct var_t *var;
2345 /* These first few initializations cannot fail. */
2346 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2347 spk_reset_default_chars();
2348 spk_reset_default_chartab();
2349 spk_strlwr(synth_name);
2350 spk_vars[0].u.n.high = vc->vc_cols;
2351 for (var = spk_vars; var->var_id != MAXVARS; var++)
2352 speakup_register_var(var);
2353 for (var = synth_time_vars;
2354 (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
2355 speakup_register_var(var);
2356 for (i = 1; spk_punc_info[i].mask != 0; i++)
2357 spk_set_mask_bits(NULL, i, 2);
2359 spk_set_key_info(spk_key_defaults, spk_key_buf);
2361 /* From here on out, initializations can fail. */
2362 err = speakup_add_virtual_keyboard();
2363 if (err)
2364 goto error_virtkeyboard;
2366 for (i = 0; i < MAX_NR_CONSOLES; i++)
2367 if (vc_cons[i].d) {
2368 err = speakup_allocate(vc_cons[i].d, GFP_KERNEL);
2369 if (err)
2370 goto error_kobjects;
2373 if (spk_quiet_boot)
2374 spk_shut_up |= 0x01;
2376 err = speakup_kobj_init();
2377 if (err)
2378 goto error_kobjects;
2380 spk_ttyio_register_ldisc();
2381 synth_init(synth_name);
2382 speakup_register_devsynth();
2384 * register_devsynth might fail, but this error is not fatal.
2385 * /dev/synth is an extra feature; the rest of Speakup
2386 * will work fine without it.
2389 err = register_keyboard_notifier(&keyboard_notifier_block);
2390 if (err)
2391 goto error_kbdnotifier;
2392 err = register_vt_notifier(&vt_notifier_block);
2393 if (err)
2394 goto error_vtnotifier;
2396 speakup_task = kthread_create(speakup_thread, NULL, "speakup");
2398 if (IS_ERR(speakup_task)) {
2399 err = PTR_ERR(speakup_task);
2400 goto error_task;
2403 set_user_nice(speakup_task, 10);
2404 wake_up_process(speakup_task);
2406 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
2407 pr_info("synth name on entry is: %s\n", synth_name);
2408 goto out;
2410 error_task:
2411 unregister_vt_notifier(&vt_notifier_block);
2413 error_vtnotifier:
2414 unregister_keyboard_notifier(&keyboard_notifier_block);
2415 del_timer(&cursor_timer);
2417 error_kbdnotifier:
2418 speakup_unregister_devsynth();
2419 mutex_lock(&spk_mutex);
2420 synth_release();
2421 mutex_unlock(&spk_mutex);
2422 speakup_kobj_exit();
2424 error_kobjects:
2425 for (i = 0; i < MAX_NR_CONSOLES; i++)
2426 kfree(speakup_console[i]);
2428 speakup_remove_virtual_keyboard();
2430 error_virtkeyboard:
2431 for (i = 0; i < MAXVARS; i++)
2432 speakup_unregister_var(i);
2434 for (i = 0; i < 256; i++) {
2435 if (spk_characters[i] != spk_default_chars[i])
2436 kfree(spk_characters[i]);
2439 spk_free_user_msgs();
2441 out:
2442 return err;
2445 module_init(speakup_init);
2446 module_exit(speakup_exit);