2 * review functions for the speakup screen review package.
3 * originally written by: Kirk Reiser and Andy Berdan.
5 * extensively modified by David Borowski.
7 ** Copyright (C) 1998 Kirk Reiser.
8 * Copyright (C) 2003 David Borowski.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <linux/kernel.h>
27 #include <linux/tty.h>
28 #include <linux/mm.h> /* __get_free_page() and friends */
29 #include <linux/vt_kern.h>
30 #include <linux/ctype.h>
31 #include <linux/selection.h>
32 #include <linux/unistd.h>
33 #include <linux/jiffies.h>
34 #include <linux/kthread.h>
35 #include <linux/keyboard.h> /* for KT_SHIFT */
36 #include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
37 #include <linux/input.h>
38 #include <linux/kmod.h>
40 /* speakup_*_selection */
41 #include <linux/module.h>
42 #include <linux/sched.h>
43 #include <linux/slab.h>
44 #include <linux/types.h>
45 #include <linux/consolemap.h>
47 #include <linux/spinlock.h>
48 #include <linux/notifier.h>
50 #include <linux/uaccess.h> /* copy_from|to|user() and others */
55 #define MAX_DELAY msecs_to_jiffies(500)
56 #define MINECHOCHAR SPACE
58 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
59 MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
60 MODULE_DESCRIPTION("Speakup console speech");
61 MODULE_LICENSE("GPL");
62 MODULE_VERSION(SPEAKUP_VERSION
);
65 module_param_named(synth
, synth_name
, charp
, S_IRUGO
);
66 module_param_named(quiet
, spk_quiet_boot
, bool, S_IRUGO
);
68 MODULE_PARM_DESC(synth
, "Synth to start if speakup is built in.");
69 MODULE_PARM_DESC(quiet
, "Do not announce when the synthesizer is found.");
71 special_func spk_special_handler
;
73 short spk_pitch_shift
, synth_flags
;
75 int spk_attrib_bleep
, spk_bleeps
, spk_bleep_time
= 10;
76 int spk_no_intr
, spk_spell_delay
;
77 int spk_key_echo
, spk_say_word_ctl
;
78 int spk_say_ctrl
, spk_bell_pos
;
80 int spk_punc_level
, spk_reading_punc
;
81 char spk_str_caps_start
[MAXVARLEN
+ 1] = "\0", spk_str_caps_stop
[MAXVARLEN
+ 1] = "\0";
82 const struct st_bits_data spk_punc_info
[] = {
84 {"some", "/$%&@", SOME
},
85 {"most", "$%&#()=+*/@^<>|\\", MOST
},
86 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC
},
87 {"delimiters", "", B_WDLM
},
88 {"repeats", "()", CH_RPT
},
89 {"extended numeric", "", B_EXNUM
},
90 {"symbols", "", B_SYM
},
94 static char mark_cut_flag
;
96 static u_char
*spk_shift_table
;
97 u_char
*spk_our_keys
[MAX_KEY
];
98 u_char spk_key_buf
[600];
99 const u_char spk_key_defaults
[] = {
100 #include "speakupmap.h"
103 /* Speakup Cursor Track Variables */
104 static int cursor_track
= 1, prev_cursor_track
= 1;
106 /* cursor track modes, must be ordered same as cursor_msgs */
114 #define read_all_mode CT_Max
116 static struct tty_struct
*tty
;
118 static void spkup_write(const char *in_buf
, int count
);
120 static char *phonetic
[] = {
121 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
122 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
124 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
125 "x ray", "yankee", "zulu"
128 /* array of 256 char pointers (one for each character description)
129 * initialized to default_chars and user selectable via
130 * /proc/speakup/characters */
131 char *spk_characters
[256];
133 char *spk_default_chars
[256] = {
134 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
135 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
136 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
137 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
139 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
141 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
144 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
146 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
147 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
148 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
149 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
150 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
153 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
154 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
155 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
156 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
157 /*127*/ "del", "control", "control", "control", "control", "control",
158 "control", "control", "control", "control", "control",
159 /*138*/ "control", "control", "control", "control", "control",
160 "control", "control", "control", "control", "control",
161 "control", "control",
162 /*150*/ "control", "control", "control", "control", "control",
163 "control", "control", "control", "control", "control",
164 /*160*/ "nbsp", "inverted bang",
165 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
166 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
167 /*172*/ "not", "soft hyphen", "registered", "macron",
168 /*176*/ "degrees", "plus or minus", "super two", "super three",
169 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
170 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
171 /*188*/ "one quarter", "one half", "three quarters",
173 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
175 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
177 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
179 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
180 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
182 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
183 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
184 /*230*/ "ae", "c cidella", "e grave", "e acute",
185 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
187 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
189 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
191 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
194 /* array of 256 u_short (one for each character)
195 * initialized to default_chartab and user selectable via
196 * /sys/module/speakup/parameters/chartab */
197 u_short spk_chartab
[256];
199 static u_short default_chartab
[256] = {
200 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 0-7 */
201 B_CTL
, B_CTL
, A_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 8-15 */
202 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /*16-23 */
203 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 24-31 */
204 WDLM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* !"#$%&' */
205 PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, A_PUNC
, A_PUNC
, PUNC
, /* ()*+, -./ */
206 NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, /* 01234567 */
207 NUM
, NUM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* 89:;<=>? */
208 PUNC
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* @ABCDEFG */
209 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* HIJKLMNO */
210 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* PQRSTUVW */
211 A_CAP
, A_CAP
, A_CAP
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, /* XYZ[\]^_ */
212 PUNC
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* `abcdefg */
213 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* hijklmno */
214 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* pqrstuvw */
215 ALPHA
, ALPHA
, ALPHA
, PUNC
, PUNC
, PUNC
, PUNC
, 0, /* xyz{|}~ */
216 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 128-134 */
218 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 136-142 */
220 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /* 144-150 */
222 B_SYM
, B_SYM
, B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /*152-158 */
224 WDLM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_CAPSYM
, /* 160-166 */
226 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 168-175 */
227 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 176-183 */
228 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 184-191 */
229 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 192-199 */
230 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 200-207 */
231 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, B_SYM
, /* 208-215 */
232 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, ALPHA
, /* 216-223 */
233 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 224-231 */
234 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 232-239 */
235 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, B_SYM
, /* 240-247 */
236 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
/* 248-255 */
239 struct task_struct
*speakup_task
;
240 struct bleep spk_unprocessed_sound
;
241 static int spk_keydown
;
242 static u_char spk_lastkey
, spk_close_press
, keymap_flags
;
243 static u_char last_keycode
, this_speakup_key
;
244 static u_long last_spk_jiffy
;
246 struct st_spk_t
*speakup_console
[MAX_NR_CONSOLES
];
248 DEFINE_MUTEX(spk_mutex
);
250 static int keyboard_notifier_call(struct notifier_block
*,
251 unsigned long code
, void *param
);
253 static struct notifier_block keyboard_notifier_block
= {
254 .notifier_call
= keyboard_notifier_call
,
257 static int vt_notifier_call(struct notifier_block
*,
258 unsigned long code
, void *param
);
260 static struct notifier_block vt_notifier_block
= {
261 .notifier_call
= vt_notifier_call
,
264 static unsigned char get_attributes(u16
*pos
)
266 return (u_char
) (scr_readw(pos
) >> 8);
269 static void speakup_date(struct vc_data
*vc
)
271 spk_x
= spk_cx
= vc
->vc_x
;
272 spk_y
= spk_cy
= vc
->vc_y
;
273 spk_pos
= spk_cp
= vc
->vc_pos
;
274 spk_old_attr
= spk_attr
;
275 spk_attr
= get_attributes((u_short
*) spk_pos
);
278 static void bleep(u_short val
)
280 static const short vals
[] = {
281 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
284 int time
= spk_bleep_time
;
285 freq
= vals
[val
% 12];
287 freq
*= (1 << (val
/ 12));
288 spk_unprocessed_sound
.freq
= freq
;
289 spk_unprocessed_sound
.jiffies
= msecs_to_jiffies(time
);
290 spk_unprocessed_sound
.active
= 1;
291 /* We can only have 1 active sound at a time. */
294 static void speakup_shut_up(struct vc_data
*vc
)
305 static void speech_kill(struct vc_data
*vc
)
307 char val
= synth
->is_alive(synth
);
311 /* re-enables synth, if disabled */
312 if (val
== 2 || spk_killed
) {
314 spk_shut_up
&= ~0x40;
315 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE
));
317 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP
));
322 static void speakup_off(struct vc_data
*vc
)
324 if (spk_shut_up
& 0x80) {
326 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER
));
329 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF
));
334 static void speakup_parked(struct vc_data
*vc
)
336 if (spk_parked
& 0x80) {
338 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED
));
341 synth_printf("%s\n", spk_msg_get(MSG_PARKED
));
345 static void speakup_cut(struct vc_data
*vc
)
347 static const char err_buf
[] = "set selection failed";
350 if (!mark_cut_flag
) {
352 spk_xs
= (u_short
) spk_x
;
353 spk_ys
= (u_short
) spk_y
;
355 synth_printf("%s\n", spk_msg_get(MSG_MARK
));
358 spk_xe
= (u_short
) spk_x
;
359 spk_ye
= (u_short
) spk_y
;
361 synth_printf("%s\n", spk_msg_get(MSG_CUT
));
363 speakup_clear_selection();
364 ret
= speakup_set_selection(tty
);
368 break; /* no error */
370 pr_warn("%sEFAULT\n", err_buf
);
373 pr_warn("%sEINVAL\n", err_buf
);
376 pr_warn("%sENOMEM\n", err_buf
);
381 static void speakup_paste(struct vc_data
*vc
)
385 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED
));
387 synth_printf("%s\n", spk_msg_get(MSG_PASTE
));
388 speakup_paste_selection(tty
);
392 static void say_attributes(struct vc_data
*vc
)
394 int fg
= spk_attr
& 0x0f;
395 int bg
= spk_attr
>> 4;
397 synth_printf("%s ", spk_msg_get(MSG_BRIGHT
));
400 synth_printf("%s", spk_msg_get(MSG_COLORS_START
+ fg
));
402 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING
));
405 synth_printf(" %s ", spk_msg_get(MSG_ON
));
406 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START
+ bg
));
417 static void announce_edge(struct vc_data
*vc
, int msg_id
)
421 if ((spk_bleeps
& 2) && (msg_id
< edge_quiet
))
422 synth_printf("%s\n", spk_msg_get(MSG_EDGE_MSGS_START
+ msg_id
- 1));
425 static void speak_char(u_char ch
)
427 char *cp
= spk_characters
[ch
];
428 struct var_t
*direct
= spk_get_var(DIRECT
);
429 if (direct
&& direct
->u
.n
.value
) {
430 if (IS_CHAR(ch
, B_CAP
)) {
432 synth_printf("%s", spk_str_caps_start
);
434 synth_printf("%c", ch
);
435 if (IS_CHAR(ch
, B_CAP
))
436 synth_printf("%s", spk_str_caps_stop
);
440 pr_info("speak_char: cp == NULL!\n");
443 synth_buffer_add(SPACE
);
444 if (IS_CHAR(ch
, B_CAP
)) {
446 synth_printf("%s", spk_str_caps_start
);
447 synth_printf("%s", cp
);
448 synth_printf("%s", spk_str_caps_stop
);
451 synth_printf("%s", spk_msg_get(MSG_CTRL
));
454 synth_printf("%s", cp
);
456 synth_buffer_add(SPACE
);
459 static u16
get_char(struct vc_data
*vc
, u16
*pos
, u_char
*attribs
)
463 u16 w
= scr_readw(pos
);
466 if (w
& vc
->vc_hi_font_mask
)
469 ch
= inverse_translate(vc
, c
, 0);
470 *attribs
= (w
& 0xff00) >> 8;
475 static void say_char(struct vc_data
*vc
)
478 spk_old_attr
= spk_attr
;
479 ch
= get_char(vc
, (u_short
*) spk_pos
, &spk_attr
);
480 if (spk_attr
!= spk_old_attr
) {
481 if (spk_attrib_bleep
& 1)
483 if (spk_attrib_bleep
& 2)
486 speak_char(ch
& 0xff);
489 static void say_phonetic_char(struct vc_data
*vc
)
492 spk_old_attr
= spk_attr
;
493 ch
= get_char(vc
, (u_short
*) spk_pos
, &spk_attr
);
494 if (isascii(ch
) && isalpha(ch
)) {
496 synth_printf("%s\n", phonetic
[--ch
]);
498 if (IS_CHAR(ch
, B_NUM
))
499 synth_printf("%s ", spk_msg_get(MSG_NUMBER
));
504 static void say_prev_char(struct vc_data
*vc
)
508 announce_edge(vc
, edge_left
);
516 static void say_next_char(struct vc_data
*vc
)
519 if (spk_x
== vc
->vc_cols
- 1) {
520 announce_edge(vc
, edge_right
);
528 /* get_word - will first check to see if the character under the
529 * reading cursor is a space and if spk_say_word_ctl is true it will
530 * return the word space. If spk_say_word_ctl is not set it will check to
531 * see if there is a word starting on the next position to the right
532 * and return that word if it exists. If it does not exist it will
533 * move left to the beginning of any previous word on the line or the
534 * beginning off the line whichever comes first.. */
536 static u_long
get_word(struct vc_data
*vc
)
538 u_long cnt
= 0, tmpx
= spk_x
, tmp_pos
= spk_pos
;
542 spk_old_attr
= spk_attr
;
543 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
, &temp
);
545 /* decided to take out the sayword if on a space (mis-information */
546 if (spk_say_word_ctl
&& ch
== SPACE
) {
548 synth_printf("%s\n", spk_msg_get(MSG_SPACE
));
550 } else if ((tmpx
< vc
->vc_cols
- 2)
551 && (ch
== SPACE
|| ch
== 0 || IS_WDLM(ch
))
552 && ((char)get_char(vc
, (u_short
*) &tmp_pos
+ 1, &temp
) >
558 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
- 1, &temp
);
559 if ((ch
== SPACE
|| ch
== 0 || IS_WDLM(ch
))
560 && ((char)get_char(vc
, (u_short
*) tmp_pos
, &temp
) >
566 attr_ch
= get_char(vc
, (u_short
*) tmp_pos
, &spk_attr
);
567 buf
[cnt
++] = attr_ch
& 0xff;
568 while (tmpx
< vc
->vc_cols
- 1) {
571 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
, &temp
);
572 if ((ch
== SPACE
) || ch
== 0
573 || (IS_WDLM(buf
[cnt
- 1]) && (ch
> SPACE
)))
581 static void say_word(struct vc_data
*vc
)
583 u_long cnt
= get_word(vc
);
584 u_short saved_punc_mask
= spk_punc_mask
;
587 spk_punc_mask
= PUNC
;
589 spkup_write(buf
, cnt
);
590 spk_punc_mask
= saved_punc_mask
;
593 static void say_prev_word(struct vc_data
*vc
)
597 u_short edge_said
= 0, last_state
= 0, state
= 0;
602 announce_edge(vc
, edge_top
);
607 edge_said
= edge_quiet
;
612 edge_said
= edge_top
;
615 if (edge_said
!= edge_quiet
)
616 edge_said
= edge_left
;
620 spk_x
= vc
->vc_cols
- 1;
624 ch
= (char)get_char(vc
, (u_short
*) spk_pos
, &temp
);
625 if (ch
== SPACE
|| ch
== 0)
627 else if (IS_WDLM(ch
))
631 if (state
< last_state
) {
638 if (spk_x
== 0 && edge_said
== edge_quiet
)
639 edge_said
= edge_left
;
640 if (edge_said
> 0 && edge_said
< edge_quiet
)
641 announce_edge(vc
, edge_said
);
645 static void say_next_word(struct vc_data
*vc
)
649 u_short edge_said
= 0, last_state
= 2, state
= 0;
652 if (spk_x
== vc
->vc_cols
- 1 && spk_y
== vc
->vc_rows
- 1) {
653 announce_edge(vc
, edge_bottom
);
657 ch
= (char)get_char(vc
, (u_short
*) spk_pos
, &temp
);
658 if (ch
== SPACE
|| ch
== 0)
660 else if (IS_WDLM(ch
))
664 if (state
> last_state
)
666 if (spk_x
>= vc
->vc_cols
- 1) {
667 if (spk_y
== vc
->vc_rows
- 1) {
668 edge_said
= edge_bottom
;
674 edge_said
= edge_right
;
681 announce_edge(vc
, edge_said
);
685 static void spell_word(struct vc_data
*vc
)
687 static char *delay_str
[] = { "", ",", ".", ". .", ". . ." };
688 char *cp
= buf
, *str_cap
= spk_str_caps_stop
;
689 char *cp1
, *last_cap
= spk_str_caps_stop
;
693 while ((ch
= (u_char
) *cp
)) {
695 synth_printf(" %s ", delay_str
[spk_spell_delay
]);
696 if (IS_CHAR(ch
, B_CAP
)) {
697 str_cap
= spk_str_caps_start
;
698 if (*spk_str_caps_stop
)
700 else /* synth has no pitch */
701 last_cap
= spk_str_caps_stop
;
703 str_cap
= spk_str_caps_stop
;
704 if (str_cap
!= last_cap
) {
705 synth_printf("%s", str_cap
);
708 if (this_speakup_key
== SPELL_PHONETIC
709 && (isascii(ch
) && isalpha(ch
))) {
711 cp1
= phonetic
[--ch
];
713 cp1
= spk_characters
[ch
];
715 synth_printf("%s", spk_msg_get(MSG_CTRL
));
719 synth_printf("%s", cp1
);
722 if (str_cap
!= spk_str_caps_stop
)
723 synth_printf("%s", spk_str_caps_stop
);
726 static int get_line(struct vc_data
*vc
)
728 u_long tmp
= spk_pos
- (spk_x
* 2);
732 spk_old_attr
= spk_attr
;
733 spk_attr
= get_attributes((u_short
*) spk_pos
);
734 for (i
= 0; i
< vc
->vc_cols
; i
++) {
735 buf
[i
] = (u_char
) get_char(vc
, (u_short
*) tmp
, &tmp2
);
738 for (--i
; i
>= 0; i
--)
744 static void say_line(struct vc_data
*vc
)
746 int i
= get_line(vc
);
748 u_short saved_punc_mask
= spk_punc_mask
;
750 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
754 if (this_speakup_key
== SAY_LINE_INDENT
) {
758 synth_printf("%d, ", (cp
- buf
) + 1);
760 spk_punc_mask
= spk_punc_masks
[spk_reading_punc
];
762 spk_punc_mask
= saved_punc_mask
;
765 static void say_prev_line(struct vc_data
*vc
)
769 announce_edge(vc
, edge_top
);
773 spk_pos
-= vc
->vc_size_row
;
777 static void say_next_line(struct vc_data
*vc
)
780 if (spk_y
== vc
->vc_rows
- 1) {
781 announce_edge(vc
, edge_bottom
);
785 spk_pos
+= vc
->vc_size_row
;
789 static int say_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
794 u_short saved_punc_mask
= spk_punc_mask
;
795 spk_old_attr
= spk_attr
;
796 spk_attr
= get_attributes((u_short
*) from
);
798 buf
[i
++] = (char)get_char(vc
, (u_short
*) from
, &tmp
);
800 if (i
>= vc
->vc_size_row
)
803 for (--i
; i
>= 0; i
--)
811 spk_punc_mask
= spk_punc_info
[spk_reading_punc
].mask
;
814 spk_punc_mask
= saved_punc_mask
;
818 static void say_line_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
821 u_long start
= vc
->vc_origin
+ (spk_y
* vc
->vc_size_row
);
822 u_long end
= start
+ (to
* 2);
824 if (say_from_to(vc
, start
, end
, read_punc
) <= 0)
825 if (cursor_track
!= read_all_mode
)
826 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
829 /* Sentence Reading Commands */
831 static int currsentence
;
832 static int numsentences
[2];
833 static char *sentbufend
[2];
834 static char *sentmarks
[2][10];
837 static char sentbuf
[2][256];
839 static int say_sentence_num(int num
, int prev
)
842 currsentence
= num
+ 1;
843 if (prev
&& --bn
== -1)
846 if (num
> numsentences
[bn
])
849 spkup_write(sentmarks
[bn
][num
], sentbufend
[bn
] - sentmarks
[bn
][num
]);
853 static int get_sentence_buf(struct vc_data
*vc
, int read_punc
)
863 start
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
);
864 end
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
) + vc
->vc_cols
* 2;
866 numsentences
[bn
] = 0;
867 sentmarks
[bn
][0] = &sentbuf
[bn
][0];
869 spk_old_attr
= spk_attr
;
870 spk_attr
= get_attributes((u_short
*) start
);
872 while (start
< end
) {
873 sentbuf
[bn
][i
] = (char)get_char(vc
, (u_short
*) start
, &tmp
);
875 if (sentbuf
[bn
][i
] == SPACE
&& sentbuf
[bn
][i
- 1] == '.'
876 && numsentences
[bn
] < 9) {
877 /* Sentence Marker */
879 sentmarks
[bn
][numsentences
[bn
]] =
885 if (i
>= vc
->vc_size_row
)
889 for (--i
; i
>= 0; i
--)
890 if (sentbuf
[bn
][i
] != SPACE
)
896 sentbuf
[bn
][++i
] = SPACE
;
897 sentbuf
[bn
][++i
] = '\0';
899 sentbufend
[bn
] = &sentbuf
[bn
][i
];
900 return numsentences
[bn
];
903 static void say_screen_from_to(struct vc_data
*vc
, u_long from
, u_long to
)
905 u_long start
= vc
->vc_origin
, end
;
907 start
+= from
* vc
->vc_size_row
;
908 if (to
> vc
->vc_rows
)
910 end
= vc
->vc_origin
+ (to
* vc
->vc_size_row
);
911 for (from
= start
; from
< end
; from
= to
) {
912 to
= from
+ vc
->vc_size_row
;
913 say_from_to(vc
, from
, to
, 1);
917 static void say_screen(struct vc_data
*vc
)
919 say_screen_from_to(vc
, 0, vc
->vc_rows
);
922 static void speakup_win_say(struct vc_data
*vc
)
924 u_long start
, end
, from
, to
;
926 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
929 start
= vc
->vc_origin
+ (win_top
* vc
->vc_size_row
);
930 end
= vc
->vc_origin
+ (win_bottom
* vc
->vc_size_row
);
931 while (start
<= end
) {
932 from
= start
+ (win_left
* 2);
933 to
= start
+ (win_right
* 2);
934 say_from_to(vc
, from
, to
, 1);
935 start
+= vc
->vc_size_row
;
939 static void top_edge(struct vc_data
*vc
)
942 spk_pos
= vc
->vc_origin
+ 2 * spk_x
;
947 static void bottom_edge(struct vc_data
*vc
)
950 spk_pos
+= (vc
->vc_rows
- spk_y
- 1) * vc
->vc_size_row
;
951 spk_y
= vc
->vc_rows
- 1;
955 static void left_edge(struct vc_data
*vc
)
958 spk_pos
-= spk_x
* 2;
963 static void right_edge(struct vc_data
*vc
)
966 spk_pos
+= (vc
->vc_cols
- spk_x
- 1) * 2;
967 spk_x
= vc
->vc_cols
- 1;
971 static void say_first_char(struct vc_data
*vc
)
973 int i
, len
= get_line(vc
);
977 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
980 for (i
= 0; i
< len
; i
++)
984 spk_pos
-= (spk_x
- i
) * 2;
986 synth_printf("%d, ", ++i
);
990 static void say_last_char(struct vc_data
*vc
)
992 int len
= get_line(vc
);
996 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1000 spk_pos
-= (spk_x
- len
) * 2;
1002 synth_printf("%d, ", ++len
);
1006 static void say_position(struct vc_data
*vc
)
1008 synth_printf(spk_msg_get(MSG_POS_INFO
), spk_y
+ 1, spk_x
+ 1,
1013 /* Added by brianb */
1014 static void say_char_num(struct vc_data
*vc
)
1017 u_short ch
= get_char(vc
, (u_short
*) spk_pos
, &tmp
);
1019 synth_printf(spk_msg_get(MSG_CHAR_INFO
), ch
, ch
);
1022 /* these are stub functions to keep keyboard.c happy. */
1024 static void say_from_top(struct vc_data
*vc
)
1026 say_screen_from_to(vc
, 0, spk_y
);
1029 static void say_to_bottom(struct vc_data
*vc
)
1031 say_screen_from_to(vc
, spk_y
, vc
->vc_rows
);
1034 static void say_from_left(struct vc_data
*vc
)
1036 say_line_from_to(vc
, 0, spk_x
, 1);
1039 static void say_to_right(struct vc_data
*vc
)
1041 say_line_from_to(vc
, spk_x
, vc
->vc_cols
, 1);
1044 /* end of stub functions. */
1046 static void spkup_write(const char *in_buf
, int count
)
1048 static int rep_count
;
1049 static u_char ch
= '\0', old_ch
= '\0';
1050 static u_short char_type
, last_type
;
1051 int in_count
= count
;
1054 if (cursor_track
== read_all_mode
) {
1055 /* Insert Sentence Index */
1056 if ((in_buf
== sentmarks
[bn
][currsentence
]) &&
1057 (currsentence
<= numsentences
[bn
]))
1058 synth_insert_next_index(currsentence
++);
1060 ch
= (u_char
) *in_buf
++;
1061 char_type
= spk_chartab
[ch
];
1062 if (ch
== old_ch
&& !(char_type
& B_NUM
)) {
1063 if (++rep_count
> 2)
1066 if ((last_type
& CH_RPT
) && rep_count
> 2) {
1068 synth_printf(spk_msg_get(MSG_REPEAT_DESC
),
1074 if (ch
== spk_lastkey
) {
1076 if (spk_key_echo
== 1 && ch
>= MINECHOCHAR
)
1078 } else if (char_type
& B_ALPHA
) {
1079 if ((synth_flags
& SF_DEC
) && (last_type
& PUNC
))
1080 synth_buffer_add(SPACE
);
1081 synth_printf("%c", ch
);
1082 } else if (char_type
& B_NUM
) {
1084 synth_printf("%c", ch
);
1085 } else if (char_type
& spk_punc_mask
) {
1087 char_type
&= ~PUNC
; /* for dec nospell processing */
1088 } else if (char_type
& SYNTH_OK
) {
1089 /* these are usually puncts like . and , which synth
1090 * needs for expression.
1091 * suppress multiple to get rid of long pauses and
1092 * clear repeat count
1094 * repeats on you don't get nothing repeated count */
1096 synth_printf("%c", ch
);
1100 /* send space and record position, if next is num overwrite space */
1102 synth_buffer_add(SPACE
);
1107 last_type
= char_type
;
1110 if (in_count
> 2 && rep_count
> 2) {
1111 if (last_type
& CH_RPT
) {
1113 synth_printf(spk_msg_get(MSG_REPEAT_DESC2
), ++rep_count
);
1120 static const int NUM_CTL_LABELS
= (MSG_CTL_END
- MSG_CTL_START
+ 1);
1122 static void read_all_doc(struct vc_data
*vc
);
1123 static void cursor_done(u_long data
);
1124 static DEFINE_TIMER(cursor_timer
, cursor_done
, 0, 0);
1126 static void do_handle_shift(struct vc_data
*vc
, u_char value
, char up_flag
)
1128 unsigned long flags
;
1129 if (synth
== NULL
|| up_flag
|| spk_killed
)
1131 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1132 if (cursor_track
== read_all_mode
) {
1135 del_timer(&cursor_timer
);
1136 spk_shut_up
&= 0xfe;
1141 del_timer(&cursor_timer
);
1142 cursor_track
= prev_cursor_track
;
1143 spk_shut_up
&= 0xfe;
1148 spk_shut_up
&= 0xfe;
1151 if (spk_say_ctrl
&& value
< NUM_CTL_LABELS
)
1152 synth_printf("%s", spk_msg_get(MSG_CTL_START
+ value
));
1153 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1156 static void do_handle_latin(struct vc_data
*vc
, u_char value
, char up_flag
)
1158 unsigned long flags
;
1159 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1161 spk_lastkey
= spk_keydown
= 0;
1162 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1165 if (synth
== NULL
|| spk_killed
) {
1166 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1169 spk_shut_up
&= 0xfe;
1170 spk_lastkey
= value
;
1173 if (spk_key_echo
== 2 && value
>= MINECHOCHAR
)
1175 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1178 int spk_set_key_info(const u_char
*key_info
, u_char
*k_buffer
)
1180 int i
= 0, states
, key_data_len
;
1181 const u_char
*cp
= key_info
;
1182 u_char
*cp1
= k_buffer
;
1183 u_char ch
, version
, num_keys
;
1185 if (version
!= KEY_MAP_VER
)
1188 states
= (int)cp
[1];
1189 key_data_len
= (states
+ 1) * (num_keys
+ 1);
1190 if (key_data_len
+ SHIFT_TBL_SIZE
+ 4 >= sizeof(spk_key_buf
))
1192 memset(k_buffer
, 0, SHIFT_TBL_SIZE
);
1193 memset(spk_our_keys
, 0, sizeof(spk_our_keys
));
1194 spk_shift_table
= k_buffer
;
1195 spk_our_keys
[0] = spk_shift_table
;
1196 cp1
+= SHIFT_TBL_SIZE
;
1197 memcpy(cp1
, cp
, key_data_len
+ 3);
1198 /* get num_keys, states and data */
1199 cp1
+= 2; /* now pointing at shift states */
1200 for (i
= 1; i
<= states
; i
++) {
1202 if (ch
>= SHIFT_TBL_SIZE
)
1204 spk_shift_table
[ch
] = i
;
1206 keymap_flags
= *cp1
++;
1207 while ((ch
= *cp1
)) {
1210 spk_our_keys
[ch
] = cp1
;
1216 static struct var_t spk_vars
[] = {
1217 /* bell must be first to set high limit */
1218 {BELL_POS
, .u
.n
= {NULL
, 0, 0, 0, 0, 0, NULL
} },
1219 {SPELL_DELAY
, .u
.n
= {NULL
, 0, 0, 4, 0, 0, NULL
} },
1220 {ATTRIB_BLEEP
, .u
.n
= {NULL
, 1, 0, 3, 0, 0, NULL
} },
1221 {BLEEPS
, .u
.n
= {NULL
, 3, 0, 3, 0, 0, NULL
} },
1222 {BLEEP_TIME
, .u
.n
= {NULL
, 30, 1, 200, 0, 0, NULL
} },
1223 {PUNC_LEVEL
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1224 {READING_PUNC
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1225 {CURSOR_TIME
, .u
.n
= {NULL
, 120, 50, 600, 0, 0, NULL
} },
1226 {SAY_CONTROL
, TOGGLE_0
},
1227 {SAY_WORD_CTL
, TOGGLE_0
},
1228 {NO_INTERRUPT
, TOGGLE_0
},
1229 {KEY_ECHO
, .u
.n
= {NULL
, 1, 0, 2, 0, 0, NULL
} },
1233 static void toggle_cursoring(struct vc_data
*vc
)
1235 if (cursor_track
== read_all_mode
)
1236 cursor_track
= prev_cursor_track
;
1237 if (++cursor_track
>= CT_Max
)
1239 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START
+ cursor_track
));
1242 void spk_reset_default_chars(void)
1246 /* First, free any non-default */
1247 for (i
= 0; i
< 256; i
++) {
1248 if ((spk_characters
[i
] != NULL
)
1249 && (spk_characters
[i
] != spk_default_chars
[i
]))
1250 kfree(spk_characters
[i
]);
1253 memcpy(spk_characters
, spk_default_chars
, sizeof(spk_default_chars
));
1256 void spk_reset_default_chartab(void)
1258 memcpy(spk_chartab
, default_chartab
, sizeof(default_chartab
));
1261 static const struct st_bits_data
*pb_edit
;
1263 static int edit_bits(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1265 short mask
= pb_edit
->mask
, ch_type
= spk_chartab
[ch
];
1266 if (type
!= KT_LATIN
|| (ch_type
& B_NUM
) || ch
< SPACE
)
1269 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE
));
1270 spk_special_handler
= NULL
;
1273 if (mask
< PUNC
&& !(ch_type
& PUNC
))
1275 spk_chartab
[ch
] ^= mask
;
1277 synth_printf(" %s\n",
1278 (spk_chartab
[ch
] & mask
) ? spk_msg_get(MSG_ON
) :
1279 spk_msg_get(MSG_OFF
));
1283 /* Allocation concurrency is protected by the console semaphore */
1284 static int speakup_allocate(struct vc_data
*vc
)
1288 vc_num
= vc
->vc_num
;
1289 if (speakup_console
[vc_num
] == NULL
) {
1290 speakup_console
[vc_num
] = kzalloc(sizeof(*speakup_console
[0]),
1292 if (speakup_console
[vc_num
] == NULL
)
1295 } else if (!spk_parked
)
1301 static void speakup_deallocate(struct vc_data
*vc
)
1305 vc_num
= vc
->vc_num
;
1306 kfree(speakup_console
[vc_num
]);
1307 speakup_console
[vc_num
] = NULL
;
1310 static u_char is_cursor
;
1311 static u_long old_cursor_pos
, old_cursor_x
, old_cursor_y
;
1312 static int cursor_con
;
1314 static void reset_highlight_buffers(struct vc_data
*);
1316 static int read_all_key
;
1318 static void start_read_all_timer(struct vc_data
*vc
, int command
);
1332 static void kbd_fakekey2(struct vc_data
*vc
, int command
)
1334 del_timer(&cursor_timer
);
1335 speakup_fake_down_arrow();
1336 start_read_all_timer(vc
, command
);
1339 static void read_all_doc(struct vc_data
*vc
)
1341 if ((vc
->vc_num
!= fg_console
) || synth
== NULL
|| spk_shut_up
)
1343 if (!synth_supports_indexing())
1345 if (cursor_track
!= read_all_mode
)
1346 prev_cursor_track
= cursor_track
;
1347 cursor_track
= read_all_mode
;
1348 spk_reset_index_count(0);
1349 if (get_sentence_buf(vc
, 0) == -1)
1350 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1352 say_sentence_num(0, 0);
1353 synth_insert_next_index(0);
1354 start_read_all_timer(vc
, RA_TIMER
);
1358 static void stop_read_all(struct vc_data
*vc
)
1360 del_timer(&cursor_timer
);
1361 cursor_track
= prev_cursor_track
;
1362 spk_shut_up
&= 0xfe;
1366 static void start_read_all_timer(struct vc_data
*vc
, int command
)
1368 struct var_t
*cursor_timeout
;
1370 cursor_con
= vc
->vc_num
;
1371 read_all_key
= command
;
1372 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1373 mod_timer(&cursor_timer
,
1374 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1377 static void handle_cursor_read_all(struct vc_data
*vc
, int command
)
1379 int indcount
, sentcount
, rv
, sn
;
1383 /* Get Current Sentence */
1384 spk_get_index_count(&indcount
, &sentcount
);
1385 /*printk("%d %d ", indcount, sentcount); */
1386 spk_reset_index_count(sentcount
+ 1);
1387 if (indcount
== 1) {
1388 if (!say_sentence_num(sentcount
+ 1, 0)) {
1389 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1392 synth_insert_next_index(0);
1395 if (!say_sentence_num(sentcount
+ 1, 1)) {
1397 spk_reset_index_count(sn
);
1399 synth_insert_next_index(0);
1400 if (!say_sentence_num(sn
, 0)) {
1401 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1404 synth_insert_next_index(0);
1406 start_read_all_timer(vc
, RA_TIMER
);
1416 if (get_sentence_buf(vc
, 0) == -1) {
1417 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1419 say_sentence_num(0, 0);
1420 synth_insert_next_index(0);
1421 start_read_all_timer(vc
, RA_TIMER
);
1424 case RA_FIND_NEXT_SENT
:
1425 rv
= get_sentence_buf(vc
, 0);
1429 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1431 say_sentence_num(1, 0);
1432 synth_insert_next_index(0);
1433 start_read_all_timer(vc
, RA_TIMER
);
1436 case RA_FIND_PREV_SENT
:
1439 spk_get_index_count(&indcount
, &sentcount
);
1441 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1443 start_read_all_timer(vc
, RA_TIMER
);
1448 static int pre_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1450 unsigned long flags
;
1451 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1452 if (cursor_track
== read_all_mode
) {
1454 if (synth
== NULL
|| up_flag
|| spk_shut_up
) {
1455 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1458 del_timer(&cursor_timer
);
1459 spk_shut_up
&= 0xfe;
1461 start_read_all_timer(vc
, value
+ 1);
1462 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1465 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1469 static void do_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1471 unsigned long flags
;
1472 struct var_t
*cursor_timeout
;
1474 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1476 if (synth
== NULL
|| up_flag
|| spk_shut_up
|| cursor_track
== CT_Off
) {
1477 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1480 spk_shut_up
&= 0xfe;
1483 /* the key press flushes if !no_inter but we want to flush on cursor
1484 * moves regardless of no_inter state */
1485 is_cursor
= value
+ 1;
1486 old_cursor_pos
= vc
->vc_pos
;
1487 old_cursor_x
= vc
->vc_x
;
1488 old_cursor_y
= vc
->vc_y
;
1489 speakup_console
[vc
->vc_num
]->ht
.cy
= vc
->vc_y
;
1490 cursor_con
= vc
->vc_num
;
1491 if (cursor_track
== CT_Highlight
)
1492 reset_highlight_buffers(vc
);
1493 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1494 mod_timer(&cursor_timer
,
1495 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1496 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1499 static void update_color_buffer(struct vc_data
*vc
, const char *ic
, int len
)
1502 int vc_num
= vc
->vc_num
;
1504 bi
= ((vc
->vc_attr
& 0x70) >> 4);
1505 hi
= speakup_console
[vc_num
]->ht
.highsize
[bi
];
1508 if (speakup_console
[vc_num
]->ht
.highsize
[bi
] == 0) {
1509 speakup_console
[vc_num
]->ht
.rpos
[bi
] = vc
->vc_pos
;
1510 speakup_console
[vc_num
]->ht
.rx
[bi
] = vc
->vc_x
;
1511 speakup_console
[vc_num
]->ht
.ry
[bi
] = vc
->vc_y
;
1513 while ((hi
< COLOR_BUFFER_SIZE
) && (i
< len
)) {
1514 if ((ic
[i
] > 32) && (ic
[i
] < 127)) {
1515 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] = ic
[i
];
1517 } else if ((ic
[i
] == 32) && (hi
!= 0)) {
1518 if (speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
- 1] !=
1520 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] =
1527 speakup_console
[vc_num
]->ht
.highsize
[bi
] = hi
;
1530 static void reset_highlight_buffers(struct vc_data
*vc
)
1533 int vc_num
= vc
->vc_num
;
1534 for (i
= 0; i
< 8; i
++)
1535 speakup_console
[vc_num
]->ht
.highsize
[i
] = 0;
1538 static int count_highlight_color(struct vc_data
*vc
)
1542 int vc_num
= vc
->vc_num
;
1544 u16
*start
= (u16
*) vc
->vc_origin
;
1546 for (i
= 0; i
< 8; i
++)
1547 speakup_console
[vc_num
]->ht
.bgcount
[i
] = 0;
1549 for (i
= 0; i
< vc
->vc_rows
; i
++) {
1550 u16
*end
= start
+ vc
->vc_cols
* 2;
1552 for (ptr
= start
; ptr
< end
; ptr
++) {
1553 ch
= get_attributes(ptr
);
1554 bg
= (ch
& 0x70) >> 4;
1555 speakup_console
[vc_num
]->ht
.bgcount
[bg
]++;
1557 start
+= vc
->vc_size_row
;
1561 for (i
= 0; i
< 8; i
++)
1562 if (speakup_console
[vc_num
]->ht
.bgcount
[i
] > 0)
1567 static int get_highlight_color(struct vc_data
*vc
)
1570 unsigned int cptr
[8], tmp
;
1571 int vc_num
= vc
->vc_num
;
1573 for (i
= 0; i
< 8; i
++)
1576 for (i
= 0; i
< 7; i
++)
1577 for (j
= i
+ 1; j
< 8; j
++)
1578 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] >
1579 speakup_console
[vc_num
]->ht
.bgcount
[cptr
[j
]]) {
1585 for (i
= 0; i
< 8; i
++)
1586 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] != 0)
1587 if (speakup_console
[vc_num
]->ht
.highsize
[cptr
[i
]] > 0)
1592 static int speak_highlight(struct vc_data
*vc
)
1595 int vc_num
= vc
->vc_num
;
1596 if (count_highlight_color(vc
) == 1)
1598 hc
= get_highlight_color(vc
);
1600 d
= vc
->vc_y
- speakup_console
[vc_num
]->ht
.cy
;
1601 if ((d
== 1) || (d
== -1))
1602 if (speakup_console
[vc_num
]->ht
.ry
[hc
] != vc
->vc_y
)
1606 spkup_write(speakup_console
[vc_num
]->ht
.highbuf
[hc
],
1607 speakup_console
[vc_num
]->ht
.highsize
[hc
]);
1608 spk_pos
= spk_cp
= speakup_console
[vc_num
]->ht
.rpos
[hc
];
1609 spk_x
= spk_cx
= speakup_console
[vc_num
]->ht
.rx
[hc
];
1610 spk_y
= spk_cy
= speakup_console
[vc_num
]->ht
.ry
[hc
];
1616 static void cursor_done(u_long data
)
1618 struct vc_data
*vc
= vc_cons
[cursor_con
].d
;
1619 unsigned long flags
;
1620 del_timer(&cursor_timer
);
1621 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1622 if (cursor_con
!= fg_console
) {
1628 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1629 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1630 spk_keydown
= is_cursor
= 0;
1634 if (cursor_track
== read_all_mode
) {
1635 handle_cursor_read_all(vc
, read_all_key
);
1638 if (cursor_track
== CT_Highlight
) {
1639 if (speak_highlight(vc
)) {
1640 spk_keydown
= is_cursor
= 0;
1644 if (cursor_track
== CT_Window
)
1645 speakup_win_say(vc
);
1646 else if (is_cursor
== 1 || is_cursor
== 4)
1647 say_line_from_to(vc
, 0, vc
->vc_cols
, 0);
1650 spk_keydown
= is_cursor
= 0;
1652 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1655 /* called by: vt_notifier_call() */
1656 static void speakup_bs(struct vc_data
*vc
)
1658 unsigned long flags
;
1659 if (!speakup_console
[vc
->vc_num
])
1661 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1662 /* Speakup output, discard */
1666 if (spk_shut_up
|| synth
== NULL
) {
1667 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1670 if (vc
->vc_num
== fg_console
&& spk_keydown
) {
1675 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1678 /* called by: vt_notifier_call() */
1679 static void speakup_con_write(struct vc_data
*vc
, const char *str
, int len
)
1681 unsigned long flags
;
1682 if ((vc
->vc_num
!= fg_console
) || spk_shut_up
|| synth
== NULL
)
1684 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1685 /* Speakup output, discard */
1687 if (spk_bell_pos
&& spk_keydown
&& (vc
->vc_x
== spk_bell_pos
- 1))
1689 if ((is_cursor
) || (cursor_track
== read_all_mode
)) {
1690 if (cursor_track
== CT_Highlight
)
1691 update_color_buffer(vc
, str
, len
);
1692 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1696 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1697 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1698 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1703 spkup_write(str
, len
);
1704 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1707 static void speakup_con_update(struct vc_data
*vc
)
1709 unsigned long flags
;
1710 if (speakup_console
[vc
->vc_num
] == NULL
|| spk_parked
)
1712 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1713 /* Speakup output, discard */
1716 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1719 static void do_handle_spec(struct vc_data
*vc
, u_char value
, char up_flag
)
1721 unsigned long flags
;
1724 if (synth
== NULL
|| up_flag
|| spk_killed
)
1726 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1727 spk_shut_up
&= 0xfe;
1732 label
= spk_msg_get(MSG_KEYNAME_CAPSLOCK
);
1733 on_off
= vt_get_leds(fg_console
, VC_CAPSLOCK
);
1736 label
= spk_msg_get(MSG_KEYNAME_NUMLOCK
);
1737 on_off
= vt_get_leds(fg_console
, VC_NUMLOCK
);
1740 label
= spk_msg_get(MSG_KEYNAME_SCROLLLOCK
);
1741 on_off
= vt_get_leds(fg_console
, VC_SCROLLOCK
);
1742 if (speakup_console
[vc
->vc_num
])
1743 speakup_console
[vc
->vc_num
]->tty_stopped
= on_off
;
1747 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1751 synth_printf("%s %s\n",
1752 label
, spk_msg_get(MSG_STATUS_START
+ on_off
));
1753 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1756 static int inc_dec_var(u_char value
)
1758 struct st_var_header
*p_header
;
1759 struct var_t
*var_data
;
1763 int var_id
= (int)value
- VAR_START
;
1764 int how
= (var_id
& 1) ? E_INC
: E_DEC
;
1765 var_id
= var_id
/ 2 + FIRST_SET_VAR
;
1766 p_header
= spk_get_var_header(var_id
);
1767 if (p_header
== NULL
)
1769 if (p_header
->var_type
!= VAR_NUM
)
1771 var_data
= p_header
->data
;
1772 if (spk_set_num_var(1, p_header
, how
) != 0)
1774 if (!spk_close_press
) {
1775 for (pn
= p_header
->name
; *pn
; pn
++) {
1782 snprintf(cp
, sizeof(num_buf
) - (cp
- num_buf
), " %d ",
1783 var_data
->u
.n
.value
);
1784 synth_printf("%s", num_buf
);
1788 static void speakup_win_set(struct vc_data
*vc
)
1791 if (win_start
> 1) {
1792 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET
));
1795 if (spk_x
< win_left
|| spk_y
< win_top
) {
1796 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START
));
1799 if (win_start
&& spk_x
== win_left
&& spk_y
== win_top
) {
1801 win_right
= vc
->vc_cols
- 1;
1803 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_LINE
),
1813 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_BOUNDARY
),
1814 (win_start
) ? spk_msg_get(MSG_END
) : spk_msg_get(MSG_START
),
1815 (int)spk_y
+ 1, (int)spk_x
+ 1);
1817 synth_printf("%s\n", info
);
1821 static void speakup_win_clear(struct vc_data
*vc
)
1823 win_top
= win_bottom
= 0;
1824 win_left
= win_right
= 0;
1826 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED
));
1829 static void speakup_win_enable(struct vc_data
*vc
)
1831 if (win_start
< 2) {
1832 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
1837 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED
));
1839 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED
));
1842 static void speakup_bits(struct vc_data
*vc
)
1844 int val
= this_speakup_key
- (FIRST_EDIT_BITS
- 1);
1845 if (spk_special_handler
!= NULL
|| val
< 1 || val
> 6) {
1846 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1849 pb_edit
= &spk_punc_info
[val
];
1850 synth_printf(spk_msg_get(MSG_EDIT_PROMPT
), pb_edit
->name
);
1851 spk_special_handler
= edit_bits
;
1854 static int handle_goto(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1856 static u_char goto_buf
[8];
1861 if (type
== KT_SPKUP
&& ch
== SPEAKUP_GOTO
)
1863 if (type
== KT_LATIN
&& ch
== '\n')
1870 ch
= goto_buf
[--num
];
1871 goto_buf
[num
] = '\0';
1872 spkup_write(&ch
, 1);
1875 if (ch
< '+' || ch
> 'y')
1877 goto_buf
[num
++] = ch
;
1878 goto_buf
[num
] = '\0';
1879 spkup_write(&ch
, 1);
1880 maxlen
= (*goto_buf
>= '0') ? 3 : 4;
1881 if ((ch
== '+' || ch
== '-') && num
== 1)
1883 if (ch
>= '0' && ch
<= '9' && num
< maxlen
)
1885 if (num
< maxlen
- 1 || num
> maxlen
)
1887 if (ch
< 'x' || ch
> 'y') {
1890 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED
));
1891 goto_buf
[num
= 0] = '\0';
1892 spk_special_handler
= NULL
;
1896 goto_pos
= simple_strtoul(goto_buf
, &cp
, 10);
1899 if (*goto_buf
< '0')
1901 else if (goto_pos
> 0)
1904 if (goto_pos
>= vc
->vc_cols
)
1905 goto_pos
= vc
->vc_cols
- 1;
1908 if (*goto_buf
< '0')
1910 else if (goto_pos
> 0)
1913 if (goto_pos
>= vc
->vc_rows
)
1914 goto_pos
= vc
->vc_rows
- 1;
1917 goto_buf
[num
= 0] = '\0';
1919 spk_special_handler
= NULL
;
1922 spk_pos
-= spk_x
* 2;
1924 spk_pos
+= goto_pos
* 2;
1928 spk_pos
= vc
->vc_origin
+ (goto_pos
* vc
->vc_size_row
);
1934 static void speakup_goto(struct vc_data
*vc
)
1936 if (spk_special_handler
!= NULL
) {
1937 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1940 synth_printf("%s\n", spk_msg_get(MSG_GOTO
));
1941 spk_special_handler
= handle_goto
;
1945 static void speakup_help(struct vc_data
*vc
)
1947 spk_handle_help(vc
, KT_SPKUP
, SPEAKUP_HELP
, 0);
1950 static void do_nothing(struct vc_data
*vc
)
1952 return; /* flush done in do_spkup */
1955 static u_char key_speakup
, spk_key_locked
;
1957 static void speakup_lock(struct vc_data
*vc
)
1959 if (!spk_key_locked
)
1960 spk_key_locked
= key_speakup
= 16;
1962 spk_key_locked
= key_speakup
= 0;
1965 typedef void (*spkup_hand
) (struct vc_data
*);
1966 static spkup_hand spkup_handler
[] = {
1967 /* must be ordered same as defines in speakup.h */
1968 do_nothing
, speakup_goto
, speech_kill
, speakup_shut_up
,
1969 speakup_cut
, speakup_paste
, say_first_char
, say_last_char
,
1970 say_char
, say_prev_char
, say_next_char
,
1971 say_word
, say_prev_word
, say_next_word
,
1972 say_line
, say_prev_line
, say_next_line
,
1973 top_edge
, bottom_edge
, left_edge
, right_edge
,
1974 spell_word
, spell_word
, say_screen
,
1975 say_position
, say_attributes
,
1976 speakup_off
, speakup_parked
, say_line
, /* this is for indent */
1977 say_from_top
, say_to_bottom
,
1978 say_from_left
, say_to_right
,
1979 say_char_num
, speakup_bits
, speakup_bits
, say_phonetic_char
,
1980 speakup_bits
, speakup_bits
, speakup_bits
,
1981 speakup_win_set
, speakup_win_clear
, speakup_win_enable
, speakup_win_say
,
1982 speakup_lock
, speakup_help
, toggle_cursoring
, read_all_doc
, NULL
1985 static void do_spkup(struct vc_data
*vc
, u_char value
)
1987 if (spk_killed
&& value
!= SPEECH_KILL
)
1991 spk_shut_up
&= 0xfe;
1992 this_speakup_key
= value
;
1993 if (value
< SPKUP_MAX_FUNC
&& spkup_handler
[value
]) {
1995 (*spkup_handler
[value
]) (vc
);
1997 if (inc_dec_var(value
) < 0)
2002 static const char *pad_chars
= "0123456789+-*/\015,.?()";
2005 speakup_key(struct vc_data
*vc
, int shift_state
, int keycode
, u_short keysym
,
2008 unsigned long flags
;
2011 u_char type
= KTYP(keysym
), value
= KVAL(keysym
), new_key
= 0;
2012 u_char shift_info
, offset
;
2017 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
2022 && (vt_get_leds(fg_console
, VC_NUMLOCK
))) {
2027 value
= spk_lastkey
= pad_chars
[value
];
2032 if (keycode
>= MAX_KEY
)
2034 key_info
= spk_our_keys
[keycode
];
2037 /* Check valid read all mode keys */
2038 if ((cursor_track
== read_all_mode
) && (!up_flag
)) {
2052 shift_info
= (shift_state
& 0x0f) + key_speakup
;
2053 offset
= spk_shift_table
[shift_info
];
2055 new_key
= key_info
[offset
];
2058 if (new_key
== SPK_KEY
) {
2059 if (!spk_key_locked
)
2060 key_speakup
= (up_flag
) ? 0 : 16;
2061 if (up_flag
|| spk_killed
)
2063 spk_shut_up
&= 0xfe;
2069 if (last_keycode
== keycode
&&
2070 last_spk_jiffy
+ MAX_DELAY
> jiffies
) {
2071 spk_close_press
= 1;
2072 offset
= spk_shift_table
[shift_info
+ 32];
2074 if (offset
&& key_info
[offset
])
2075 new_key
= key_info
[offset
];
2077 last_keycode
= keycode
;
2078 last_spk_jiffy
= jiffies
;
2084 if (type
== KT_SPKUP
&& spk_special_handler
== NULL
) {
2085 do_spkup(vc
, new_key
);
2086 spk_close_press
= 0;
2090 if (up_flag
|| spk_killed
|| type
== KT_SHIFT
)
2092 spk_shut_up
&= 0xfe;
2093 kh
= (value
== KVAL(K_DOWN
))
2094 || (value
== KVAL(K_UP
))
2095 || (value
== KVAL(K_LEFT
))
2096 || (value
== KVAL(K_RIGHT
));
2097 if ((cursor_track
!= read_all_mode
) || !kh
)
2100 if (spk_special_handler
) {
2101 if (type
== KT_SPEC
&& value
== 1) {
2104 } else if (type
== KT_LETTER
)
2106 else if (value
== 0x7f)
2107 value
= 8; /* make del = backspace */
2108 ret
= (*spk_special_handler
) (vc
, type
, value
, keycode
);
2109 spk_close_press
= 0;
2116 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
2120 static int keyboard_notifier_call(struct notifier_block
*nb
,
2121 unsigned long code
, void *_param
)
2123 struct keyboard_notifier_param
*param
= _param
;
2124 struct vc_data
*vc
= param
->vc
;
2125 int up
= !param
->down
;
2126 int ret
= NOTIFY_OK
;
2127 static int keycode
; /* to hold the current keycode */
2129 if (vc
->vc_mode
== KD_GRAPHICS
)
2133 * First, determine whether we are handling a fake keypress on
2134 * the current processor. If we are, then return NOTIFY_OK,
2135 * to pass the keystroke up the chain. This prevents us from
2136 * trying to take the Speakup lock while it is held by the
2137 * processor on which the simulated keystroke was generated.
2138 * Also, the simulated keystrokes should be ignored by Speakup.
2141 if (speakup_fake_key_pressed())
2146 /* speakup requires keycode and keysym currently */
2147 keycode
= param
->value
;
2149 case KBD_UNBOUND_KEYCODE
:
2156 if (speakup_key(vc
, param
->shift
, keycode
, param
->value
, up
))
2158 else if (KTYP(param
->value
) == KT_CUR
)
2159 ret
= pre_handle_cursor(vc
, KVAL(param
->value
), up
);
2161 case KBD_POST_KEYSYM
:{
2162 unsigned char type
= KTYP(param
->value
) - 0xf0;
2163 unsigned char val
= KVAL(param
->value
);
2166 do_handle_shift(vc
, val
, up
);
2170 do_handle_latin(vc
, val
, up
);
2173 do_handle_cursor(vc
, val
, up
);
2176 do_handle_spec(vc
, val
, up
);
2185 static int vt_notifier_call(struct notifier_block
*nb
,
2186 unsigned long code
, void *_param
)
2188 struct vt_notifier_param
*param
= _param
;
2189 struct vc_data
*vc
= param
->vc
;
2192 if (vc
->vc_mode
== KD_TEXT
)
2193 speakup_allocate(vc
);
2196 speakup_deallocate(vc
);
2199 if (param
->c
== '\b')
2201 else if (param
->c
< 0x100) {
2203 speakup_con_write(vc
, &d
, 1);
2207 speakup_con_update(vc
);
2213 /* called by: module_exit() */
2214 static void __exit
speakup_exit(void)
2218 unregister_keyboard_notifier(&keyboard_notifier_block
);
2219 unregister_vt_notifier(&vt_notifier_block
);
2220 speakup_unregister_devsynth();
2221 speakup_cancel_paste();
2222 del_timer(&cursor_timer
);
2223 kthread_stop(speakup_task
);
2224 speakup_task
= NULL
;
2225 mutex_lock(&spk_mutex
);
2227 mutex_unlock(&spk_mutex
);
2229 speakup_kobj_exit();
2231 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2232 kfree(speakup_console
[i
]);
2234 speakup_remove_virtual_keyboard();
2236 for (i
= 0; i
< MAXVARS
; i
++)
2237 speakup_unregister_var(i
);
2239 for (i
= 0; i
< 256; i
++) {
2240 if (spk_characters
[i
] != spk_default_chars
[i
])
2241 kfree(spk_characters
[i
]);
2244 spk_free_user_msgs();
2247 /* call by: module_init() */
2248 static int __init
speakup_init(void)
2252 struct st_spk_t
*first_console
;
2253 struct vc_data
*vc
= vc_cons
[fg_console
].d
;
2256 /* These first few initializations cannot fail. */
2257 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2258 spk_reset_default_chars();
2259 spk_reset_default_chartab();
2260 spk_strlwr(synth_name
);
2261 spk_vars
[0].u
.n
.high
= vc
->vc_cols
;
2262 for (var
= spk_vars
; var
->var_id
!= MAXVARS
; var
++)
2263 speakup_register_var(var
);
2264 for (var
= synth_time_vars
;
2265 (var
->var_id
>= 0) && (var
->var_id
< MAXVARS
); var
++)
2266 speakup_register_var(var
);
2267 for (i
= 1; spk_punc_info
[i
].mask
!= 0; i
++)
2268 spk_set_mask_bits(NULL
, i
, 2);
2270 spk_set_key_info(spk_key_defaults
, spk_key_buf
);
2272 /* From here on out, initializations can fail. */
2273 err
= speakup_add_virtual_keyboard();
2275 goto error_virtkeyboard
;
2277 first_console
= kzalloc(sizeof(*first_console
), GFP_KERNEL
);
2278 if (!first_console
) {
2283 speakup_console
[vc
->vc_num
] = first_console
;
2286 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2288 err
= speakup_allocate(vc_cons
[i
].d
);
2290 goto error_kobjects
;
2294 spk_shut_up
|= 0x01;
2296 err
= speakup_kobj_init();
2298 goto error_kobjects
;
2300 synth_init(synth_name
);
2301 speakup_register_devsynth();
2303 * register_devsynth might fail, but this error is not fatal.
2304 * /dev/synth is an extra feature; the rest of Speakup
2305 * will work fine without it.
2308 err
= register_keyboard_notifier(&keyboard_notifier_block
);
2310 goto error_kbdnotifier
;
2311 err
= register_vt_notifier(&vt_notifier_block
);
2313 goto error_vtnotifier
;
2315 speakup_task
= kthread_create(speakup_thread
, NULL
, "speakup");
2317 if (IS_ERR(speakup_task
)) {
2318 err
= PTR_ERR(speakup_task
);
2322 set_user_nice(speakup_task
, 10);
2323 wake_up_process(speakup_task
);
2325 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION
);
2326 pr_info("synth name on entry is: %s\n", synth_name
);
2330 unregister_vt_notifier(&vt_notifier_block
);
2333 unregister_keyboard_notifier(&keyboard_notifier_block
);
2334 del_timer(&cursor_timer
);
2337 speakup_unregister_devsynth();
2338 mutex_lock(&spk_mutex
);
2340 mutex_unlock(&spk_mutex
);
2341 speakup_kobj_exit();
2344 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2345 kfree(speakup_console
[i
]);
2348 speakup_remove_virtual_keyboard();
2351 for (i
= 0; i
< MAXVARS
; i
++)
2352 speakup_unregister_var(i
);
2354 for (i
= 0; i
< 256; i
++) {
2355 if (spk_characters
[i
] != spk_default_chars
[i
])
2356 kfree(spk_characters
[i
]);
2359 spk_free_user_msgs();
2365 module_init(speakup_init
);
2366 module_exit(speakup_exit
);