2500 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* speakup.c3 * review functions for the speakup screen review package.4 * originally written by: Kirk Reiser and Andy Berdan.5 *6 * extensively modified by David Borowski.7 *8 ** Copyright (C) 1998 Kirk Reiser.9 * Copyright (C) 2003 David Borowski.10 */11 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>26 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>33 34#include <linux/spinlock.h>35#include <linux/notifier.h>36 37#include <linux/uaccess.h> /* copy_from|to|user() and others */38 39#include "spk_priv.h"40#include "speakup.h"41 42#define MAX_DELAY msecs_to_jiffies(500)43#define MINECHOCHAR SPACE44 45MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");46MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");47MODULE_DESCRIPTION("Speakup console speech");48MODULE_LICENSE("GPL");49MODULE_VERSION(SPEAKUP_VERSION);50 51char *synth_name;52module_param_named(synth, synth_name, charp, 0444);53module_param_named(quiet, spk_quiet_boot, bool, 0444);54 55MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");56MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");57 58special_func spk_special_handler;59 60short spk_pitch_shift, synth_flags;61static u16 buf[256];62int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;63int spk_no_intr, spk_spell_delay;64int spk_key_echo, spk_say_word_ctl;65int spk_say_ctrl, spk_bell_pos;66short spk_punc_mask;67int spk_punc_level, spk_reading_punc;68int spk_cur_phonetic;69char spk_str_caps_start[MAXVARLEN + 1] = "\0";70char spk_str_caps_stop[MAXVARLEN + 1] = "\0";71char spk_str_pause[MAXVARLEN + 1] = "\0";72bool spk_paused;73const struct st_bits_data spk_punc_info[] = {74 {"none", "", 0},75 {"some", "/$%&@", SOME},76 {"most", "$%&#()=+*/@^<>|\\", MOST},77 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC},78 {"delimiters", "", B_WDLM},79 {"repeats", "()", CH_RPT},80 {"extended numeric", "", B_EXNUM},81 {"symbols", "", B_SYM},82 {NULL, NULL}83};84 85static char mark_cut_flag;86#define MAX_KEY 16087static u_char *spk_shift_table;88u_char *spk_our_keys[MAX_KEY];89u_char spk_key_buf[600];90const u_char spk_key_defaults[] = {91#include "speakupmap.h"92};93 94/* cursor track modes, must be ordered same as cursor_msgs in enum msg_index_t */95enum cursor_track {96 CT_Off = 0,97 CT_On,98 CT_Highlight,99 CT_Window,100 CT_Max,101 read_all_mode = CT_Max,102};103 104/* Speakup Cursor Track Variables */105static enum cursor_track cursor_track = 1, prev_cursor_track = 1;106 107static struct tty_struct *tty;108 109static void spkup_write(const u16 *in_buf, int count);110 111static char *phonetic[] = {112 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",113 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",114 "papa",115 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",116 "x ray", "yankee", "zulu"117};118 119/* array of 256 char pointers (one for each character description)120 * initialized to default_chars and user selectable via121 * /proc/speakup/characters122 */123char *spk_characters[256];124 125char *spk_default_chars[256] = {126/*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",127/*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",128/*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",129/*024*/ "^x", "^y", "^z", "control", "control", "control", "control",130 "control",131/*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",132 "tick",133/*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",134 "dot",135 "slash",136/*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",137 "eight", "nine",138/*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",139/*065*/ "EIGH", "B", "C", "D", "E", "F", "G",140/*072*/ "H", "I", "J", "K", "L", "M", "N", "O",141/*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",142/*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",143 "caret",144 "line",145/*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",146/*104*/ "h", "i", "j", "k", "l", "m", "n", "o",147/*112*/ "p", "q", "r", "s", "t", "u", "v", "w",148/*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",149/*127*/ "del", "control", "control", "control", "control", "control",150 "control", "control", "control", "control", "control",151/*138*/ "control", "control", "control", "control", "control",152 "control", "control", "control", "control", "control",153 "control", "control",154/*150*/ "control", "control", "control", "control", "control",155 "control", "control", "control", "control", "control",156/*160*/ "nbsp", "inverted bang",157/*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",158/*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",159/*172*/ "not", "soft hyphen", "registered", "macron",160/*176*/ "degrees", "plus or minus", "super two", "super three",161/*180*/ "acute accent", "micro", "pilcrow", "middle dot",162/*184*/ "cedilla", "super one", "male ordinal", "double right angle",163/*188*/ "one quarter", "one half", "three quarters",164 "inverted question",165/*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",166 "A RING",167/*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",168 "E OOMLAUT",169/*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",170 "N TILDE",171/*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",172/*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",173 "U CIRCUMFLEX",174/*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",175/*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",176/*230*/ "ae", "c cidella", "e grave", "e acute",177/*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",178 "i circumflex",179/*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",180 "o circumflex",181/*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",182 "u acute",183/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"184};185 186/* array of 256 u_short (one for each character)187 * initialized to default_chartab and user selectable via188 * /sys/module/speakup/parameters/chartab189 */190u_short spk_chartab[256];191 192static u_short default_chartab[256] = {193 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */194 B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */195 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */196 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */197 WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */198 PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */199 NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */200 NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */201 PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */202 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */203 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */204 A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */205 PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */206 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */207 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */208 ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */209 B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */210 B_SYM, /* 135 */211 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */212 B_CAPSYM, /* 143 */213 B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */214 B_SYM, /* 151 */215 B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */216 B_SYM, /* 159 */217 WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */218 B_SYM, /* 167 */219 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */220 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */221 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */222 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */223 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */224 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */225 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */226 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */227 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */228 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */229 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */230};231 232struct task_struct *speakup_task;233struct bleep spk_unprocessed_sound;234static int spk_keydown;235static u16 spk_lastkey;236static u_char spk_close_press, keymap_flags;237static u_char last_keycode, this_speakup_key;238static u_long last_spk_jiffy;239 240struct st_spk_t *speakup_console[MAX_NR_CONSOLES];241 242DEFINE_MUTEX(spk_mutex);243 244static int keyboard_notifier_call(struct notifier_block *,245 unsigned long code, void *param);246 247static struct notifier_block keyboard_notifier_block = {248 .notifier_call = keyboard_notifier_call,249};250 251static int vt_notifier_call(struct notifier_block *,252 unsigned long code, void *param);253 254static struct notifier_block vt_notifier_block = {255 .notifier_call = vt_notifier_call,256};257 258static unsigned char get_attributes(struct vc_data *vc, u16 *pos)259{260 pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);261 return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8;262}263 264static void speakup_date(struct vc_data *vc)265{266 spk_x = spk_cx = vc->state.x;267 spk_y = spk_cy = vc->state.y;268 spk_pos = spk_cp = vc->vc_pos;269 spk_old_attr = spk_attr;270 spk_attr = get_attributes(vc, (u_short *)spk_pos);271}272 273static void bleep(u_short val)274{275 static const short vals[] = {276 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659277 };278 short freq;279 int time = spk_bleep_time;280 281 freq = vals[val % 12];282 if (val > 11)283 freq *= (1 << (val / 12));284 spk_unprocessed_sound.freq = freq;285 spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);286 spk_unprocessed_sound.active = 1;287 /* We can only have 1 active sound at a time. */288}289 290static void speakup_shut_up(struct vc_data *vc)291{292 if (spk_killed)293 return;294 spk_shut_up |= 0x01;295 spk_parked &= 0xfe;296 speakup_date(vc);297 if (synth)298 spk_do_flush();299}300 301static void speech_kill(struct vc_data *vc)302{303 char val = synth->is_alive(synth);304 305 if (val == 0)306 return;307 308 /* re-enables synth, if disabled */309 if (val == 2 || spk_killed) {310 /* dead */311 spk_shut_up &= ~0x40;312 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));313 } else {314 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));315 spk_shut_up |= 0x40;316 }317}318 319static void speakup_off(struct vc_data *vc)320{321 if (spk_shut_up & 0x80) {322 spk_shut_up &= 0x7f;323 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));324 } else {325 spk_shut_up |= 0x80;326 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));327 }328 speakup_date(vc);329}330 331static void speakup_parked(struct vc_data *vc)332{333 if (spk_parked & 0x80) {334 spk_parked = 0;335 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));336 } else {337 spk_parked |= 0x80;338 synth_printf("%s\n", spk_msg_get(MSG_PARKED));339 }340}341 342static void speakup_cut(struct vc_data *vc)343{344 static const char err_buf[] = "set selection failed";345 int ret;346 347 if (!mark_cut_flag) {348 mark_cut_flag = 1;349 spk_xs = (u_short)spk_x;350 spk_ys = (u_short)spk_y;351 spk_sel_cons = vc;352 synth_printf("%s\n", spk_msg_get(MSG_MARK));353 return;354 }355 spk_xe = (u_short)spk_x;356 spk_ye = (u_short)spk_y;357 mark_cut_flag = 0;358 synth_printf("%s\n", spk_msg_get(MSG_CUT));359 360 ret = speakup_set_selection(tty);361 362 switch (ret) {363 case 0:364 break; /* no error */365 case -EFAULT:366 pr_warn("%sEFAULT\n", err_buf);367 break;368 case -EINVAL:369 pr_warn("%sEINVAL\n", err_buf);370 break;371 case -ENOMEM:372 pr_warn("%sENOMEM\n", err_buf);373 break;374 }375}376 377static void speakup_paste(struct vc_data *vc)378{379 if (mark_cut_flag) {380 mark_cut_flag = 0;381 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));382 } else {383 synth_printf("%s\n", spk_msg_get(MSG_PASTE));384 speakup_paste_selection(tty);385 }386}387 388static void say_attributes(struct vc_data *vc)389{390 int fg = spk_attr & 0x0f;391 int bg = spk_attr >> 4;392 393 synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));394 if (bg > 7) {395 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));396 bg -= 8;397 } else {398 synth_printf(" %s ", spk_msg_get(MSG_ON));399 }400 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));401}402 403/* must be ordered same as edge_msgs in enum msg_index_t */404enum edge {405 edge_none = 0,406 edge_top,407 edge_bottom,408 edge_left,409 edge_right,410 edge_quiet411};412 413static void announce_edge(struct vc_data *vc, enum edge msg_id)414{415 if (spk_bleeps & 1)416 bleep(spk_y);417 if ((spk_bleeps & 2) && (msg_id < edge_quiet))418 synth_printf("%s\n",419 spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));420}421 422static void speak_char(u16 ch)423{424 char *cp;425 struct var_t *direct = spk_get_var(DIRECT);426 427 if (ch >= 0x100 || (direct && direct->u.n.value)) {428 if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {429 spk_pitch_shift++;430 synth_printf("%s", spk_str_caps_start);431 }432 synth_putwc_s(ch);433 if (ch < 0x100 && IS_CHAR(ch, B_CAP))434 synth_printf("%s", spk_str_caps_stop);435 return;436 }437 438 cp = spk_characters[ch];439 if (!cp) {440 pr_info("%s: cp == NULL!\n", __func__);441 return;442 }443 if (IS_CHAR(ch, B_CAP)) {444 spk_pitch_shift++;445 synth_printf("%s %s %s",446 spk_str_caps_start, cp, spk_str_caps_stop);447 } else {448 if (*cp == '^') {449 cp++;450 synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp);451 } else {452 synth_printf(" %s ", cp);453 }454 }455}456 457static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs)458{459 u16 ch = ' ';460 461 if (vc && pos) {462 u16 w;463 u16 c;464 465 pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);466 w = scr_readw(pos);467 c = w & 0xff;468 469 if (w & vc->vc_hi_font_mask) {470 w &= ~vc->vc_hi_font_mask;471 c |= 0x100;472 }473 474 ch = inverse_translate(vc, c, true);475 *attribs = (w & 0xff00) >> 8;476 }477 return ch;478}479 480static void say_char(struct vc_data *vc)481{482 u16 ch;483 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);491 }492 speak_char(ch);493}494 495static void say_phonetic_char(struct vc_data *vc)496{497 u16 ch;498 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);508 }509}510 511static void say_prev_char(struct vc_data *vc)512{513 spk_parked |= 0x01;514 if (spk_x == 0) {515 announce_edge(vc, edge_left);516 return;517 }518 spk_x--;519 spk_pos -= 2;520 say_char(vc);521}522 523static void say_next_char(struct vc_data *vc)524{525 spk_parked |= 0x01;526 if (spk_x == vc->vc_cols - 1) {527 announce_edge(vc, edge_right);528 return;529 }530 spk_x++;531 spk_pos += 2;532 say_char(vc);533}534 535/* get_word - will first check to see if the character under the536 * reading cursor is a space and if spk_say_word_ctl is true it will537 * return the word space. If spk_say_word_ctl is not set it will check to538 * see if there is a word starting on the next position to the right539 * and return that word if it exists. If it does not exist it will540 * move left to the beginning of any previous word on the line or the541 * beginning off the line whichever comes first..542 */543 544static u_long get_word(struct vc_data *vc)545{546 u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;547 u16 ch;548 u16 attr_ch;549 u_char temp;550 551 spk_old_attr = spk_attr;552 ch = get_char(vc, (u_short *)tmp_pos, &temp);553 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--;573 }574 }575 attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);576 buf[cnt++] = attr_ch;577 while (tmpx < vc->vc_cols - 1 && cnt < ARRAY_SIZE(buf) - 1) {578 tmp_pos += 2;579 tmpx++;580 ch = get_char(vc, (u_short *)tmp_pos, &temp);581 if (ch == SPACE || ch == 0 ||582 (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) &&583 ch > SPACE))584 break;585 buf[cnt++] = ch;586 }587 buf[cnt] = '\0';588 return cnt;589}590 591static void say_word(struct vc_data *vc)592{593 u_long cnt = get_word(vc);594 u_short saved_punc_mask = spk_punc_mask;595 596 if (cnt == 0)597 return;598 spk_punc_mask = PUNC;599 buf[cnt++] = SPACE;600 spkup_write(buf, cnt);601 spk_punc_mask = saved_punc_mask;602}603 604static void say_prev_word(struct vc_data *vc)605{606 u_char temp;607 u16 ch;608 enum edge edge_said = edge_none;609 u_short last_state = 0, state = 0;610 611 spk_parked |= 0x01;612 613 if (spk_x == 0) {614 if (spk_y == 0) {615 announce_edge(vc, edge_top);616 return;617 }618 spk_y--;619 spk_x = vc->vc_cols;620 edge_said = edge_quiet;621 }622 while (1) {623 if (spk_x == 0) {624 if (spk_y == 0) {625 edge_said = edge_top;626 break;627 }628 if (edge_said != edge_quiet)629 edge_said = edge_left;630 if (state > 0)631 break;632 spk_y--;633 spk_x = vc->vc_cols - 1;634 } else {635 spk_x--;636 }637 spk_pos -= 2;638 ch = get_char(vc, (u_short *)spk_pos, &temp);639 if (ch == SPACE || ch == 0)640 state = 0;641 else if (ch < 0x100 && IS_WDLM(ch))642 state = 1;643 else644 state = 2;645 if (state < last_state) {646 spk_pos += 2;647 spk_x++;648 break;649 }650 last_state = state;651 }652 if (spk_x == 0 && edge_said == edge_quiet)653 edge_said = edge_left;654 if (edge_said > edge_none && edge_said < edge_quiet)655 announce_edge(vc, edge_said);656 say_word(vc);657}658 659static void say_next_word(struct vc_data *vc)660{661 u_char temp;662 u16 ch;663 enum edge edge_said = edge_none;664 u_short last_state = 2, state = 0;665 666 spk_parked |= 0x01;667 if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {668 announce_edge(vc, edge_bottom);669 return;670 }671 while (1) {672 ch = get_char(vc, (u_short *)spk_pos, &temp);673 if (ch == SPACE || ch == 0)674 state = 0;675 else if (ch < 0x100 && IS_WDLM(ch))676 state = 1;677 else678 state = 2;679 if (state > last_state)680 break;681 if (spk_x >= vc->vc_cols - 1) {682 if (spk_y == vc->vc_rows - 1) {683 edge_said = edge_bottom;684 break;685 }686 state = 0;687 spk_y++;688 spk_x = 0;689 edge_said = edge_right;690 } else {691 spk_x++;692 }693 spk_pos += 2;694 last_state = state;695 }696 if (edge_said > edge_none)697 announce_edge(vc, edge_said);698 say_word(vc);699}700 701static void spell_word(struct vc_data *vc)702{703 static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };704 u16 *cp = buf;705 char *cp1;706 char *str_cap = spk_str_caps_stop;707 char *last_cap = spk_str_caps_stop;708 struct var_t *direct = spk_get_var(DIRECT);709 u16 ch;710 711 if (!get_word(vc))712 return;713 while ((ch = *cp)) {714 if (cp != buf)715 synth_printf(" %s ", delay_str[spk_spell_delay]);716 /* FIXME: Non-latin1 considered as lower case */717 if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {718 str_cap = spk_str_caps_start;719 if (*spk_str_caps_stop)720 spk_pitch_shift++;721 else /* synth has no pitch */722 last_cap = spk_str_caps_stop;723 } else {724 str_cap = spk_str_caps_stop;725 }726 if (str_cap != last_cap) {727 synth_printf("%s", str_cap);728 last_cap = str_cap;729 }730 if (ch >= 0x100 || (direct && direct->u.n.value)) {731 synth_putwc_s(ch);732 } else if (this_speakup_key == SPELL_PHONETIC &&733 ch <= 0x7f && isalpha(ch)) {734 ch &= 0x1f;735 cp1 = phonetic[--ch];736 synth_printf("%s", cp1);737 } else {738 cp1 = spk_characters[ch];739 if (*cp1 == '^') {740 synth_printf("%s", spk_msg_get(MSG_CTRL));741 cp1++;742 }743 synth_printf("%s", cp1);744 }745 cp++;746 }747 if (str_cap != spk_str_caps_stop)748 synth_printf("%s", spk_str_caps_stop);749}750 751static int get_line(struct vc_data *vc)752{753 u_long tmp = spk_pos - (spk_x * 2);754 int i = 0;755 u_char tmp2;756 757 spk_old_attr = spk_attr;758 spk_attr = get_attributes(vc, (u_short *)spk_pos);759 for (i = 0; i < vc->vc_cols; i++) {760 buf[i] = get_char(vc, (u_short *)tmp, &tmp2);761 tmp += 2;762 }763 for (--i; i >= 0; i--)764 if (buf[i] != SPACE)765 break;766 return ++i;767}768 769static void say_line(struct vc_data *vc)770{771 int i = get_line(vc);772 u16 *cp;773 u_short saved_punc_mask = spk_punc_mask;774 775 if (i == 0) {776 synth_printf("%s\n", spk_msg_get(MSG_BLANK));777 return;778 }779 buf[i++] = '\n';780 if (this_speakup_key == SAY_LINE_INDENT) {781 cp = buf;782 while (*cp == SPACE)783 cp++;784 synth_printf("%zd, ", (cp - buf) + 1);785 }786 spk_punc_mask = spk_punc_masks[spk_reading_punc];787 spkup_write(buf, i);788 spk_punc_mask = saved_punc_mask;789}790 791static void say_prev_line(struct vc_data *vc)792{793 spk_parked |= 0x01;794 if (spk_y == 0) {795 announce_edge(vc, edge_top);796 return;797 }798 spk_y--;799 spk_pos -= vc->vc_size_row;800 say_line(vc);801}802 803static void say_next_line(struct vc_data *vc)804{805 spk_parked |= 0x01;806 if (spk_y == vc->vc_rows - 1) {807 announce_edge(vc, edge_bottom);808 return;809 }810 spk_y++;811 spk_pos += vc->vc_size_row;812 say_line(vc);813}814 815static int say_from_to(struct vc_data *vc, u_long from, u_long to,816 int read_punc)817{818 int i = 0;819 u_char tmp;820 u_short saved_punc_mask = spk_punc_mask;821 822 spk_old_attr = spk_attr;823 spk_attr = get_attributes(vc, (u_short *)from);824 while (from < to) {825 buf[i++] = get_char(vc, (u_short *)from, &tmp);826 from += 2;827 if (i >= vc->vc_size_row)828 break;829 }830 for (--i; i >= 0; i--)831 if (buf[i] != SPACE)832 break;833 buf[++i] = SPACE;834 buf[++i] = '\0';835 if (i < 1)836 return i;837 if (read_punc)838 spk_punc_mask = spk_punc_info[spk_reading_punc].mask;839 spkup_write(buf, i);840 if (read_punc)841 spk_punc_mask = saved_punc_mask;842 return i - 1;843}844 845static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,846 int read_punc)847{848 u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);849 u_long end = start + (to * 2);850 851 start += from * 2;852 if (say_from_to(vc, start, end, read_punc) <= 0)853 if (cursor_track != read_all_mode)854 synth_printf("%s\n", spk_msg_get(MSG_BLANK));855}856 857/* Sentence Reading Commands */858 859static int currsentence;860static int numsentences[2];861static u16 *sentbufend[2];862static u16 *sentmarks[2][10];863static int currbuf;864static int bn;865static u16 sentbuf[2][256];866 867static int say_sentence_num(int num, int prev)868{869 bn = currbuf;870 currsentence = num + 1;871 if (prev && --bn == -1)872 bn = 1;873 874 if (num > numsentences[bn])875 return 0;876 877 spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);878 return 1;879}880 881static int get_sentence_buf(struct vc_data *vc, int read_punc)882{883 u_long start, end;884 int i, bn;885 u_char tmp;886 887 currbuf++;888 if (currbuf == 2)889 currbuf = 0;890 bn = currbuf;891 start = vc->vc_origin + ((spk_y) * vc->vc_size_row);892 end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;893 894 numsentences[bn] = 0;895 sentmarks[bn][0] = &sentbuf[bn][0];896 i = 0;897 spk_old_attr = spk_attr;898 spk_attr = get_attributes(vc, (u_short *)start);899 900 while (start < end) {901 sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);902 if (i > 0) {903 if (sentbuf[bn][i] == SPACE &&904 sentbuf[bn][i - 1] == '.' &&905 numsentences[bn] < 9) {906 /* Sentence Marker */907 numsentences[bn]++;908 sentmarks[bn][numsentences[bn]] =909 &sentbuf[bn][i];910 }911 }912 i++;913 start += 2;914 if (i >= vc->vc_size_row)915 break;916 }917 918 for (--i; i >= 0; i--)919 if (sentbuf[bn][i] != SPACE)920 break;921 922 if (i < 1)923 return -1;924 925 sentbuf[bn][++i] = SPACE;926 sentbuf[bn][++i] = '\0';927 928 sentbufend[bn] = &sentbuf[bn][i];929 return numsentences[bn];930}931 932static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)933{934 u_long start = vc->vc_origin, end;935 936 if (from > 0)937 start += from * vc->vc_size_row;938 if (to > vc->vc_rows)939 to = vc->vc_rows;940 end = vc->vc_origin + (to * vc->vc_size_row);941 for (from = start; from < end; from = to) {942 to = from + vc->vc_size_row;943 say_from_to(vc, from, to, 1);944 }945}946 947static void say_screen(struct vc_data *vc)948{949 say_screen_from_to(vc, 0, vc->vc_rows);950}951 952static void speakup_win_say(struct vc_data *vc)953{954 u_long start, end, from, to;955 956 if (win_start < 2) {957 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));958 return;959 }960 start = vc->vc_origin + (win_top * vc->vc_size_row);961 end = vc->vc_origin + (win_bottom * vc->vc_size_row);962 while (start <= end) {963 from = start + (win_left * 2);964 to = start + (win_right * 2);965 say_from_to(vc, from, to, 1);966 start += vc->vc_size_row;967 }968}969 970static void top_edge(struct vc_data *vc)971{972 spk_parked |= 0x01;973 spk_pos = vc->vc_origin + 2 * spk_x;974 spk_y = 0;975 say_line(vc);976}977 978static void bottom_edge(struct vc_data *vc)979{980 spk_parked |= 0x01;981 spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;982 spk_y = vc->vc_rows - 1;983 say_line(vc);984}985 986static void left_edge(struct vc_data *vc)987{988 spk_parked |= 0x01;989 spk_pos -= spk_x * 2;990 spk_x = 0;991 say_char(vc);992}993 994static void right_edge(struct vc_data *vc)995{996 spk_parked |= 0x01;997 spk_pos += (vc->vc_cols - spk_x - 1) * 2;998 spk_x = vc->vc_cols - 1;999 say_char(vc);1000}1001 1002static void say_first_char(struct vc_data *vc)1003{1004 int i, len = get_line(vc);1005 u16 ch;1006 1007 spk_parked |= 0x01;1008 if (len == 0) {1009 synth_printf("%s\n", spk_msg_get(MSG_BLANK));1010 return;1011 }1012 for (i = 0; i < len; i++)1013 if (buf[i] != SPACE)1014 break;1015 ch = buf[i];1016 spk_pos -= (spk_x - i) * 2;1017 spk_x = i;1018 synth_printf("%d, ", ++i);1019 speak_char(ch);1020}1021 1022static void say_last_char(struct vc_data *vc)1023{1024 int len = get_line(vc);1025 u16 ch;1026 1027 spk_parked |= 0x01;1028 if (len == 0) {1029 synth_printf("%s\n", spk_msg_get(MSG_BLANK));1030 return;1031 }1032 ch = buf[--len];1033 spk_pos -= (spk_x - len) * 2;1034 spk_x = len;1035 synth_printf("%d, ", ++len);1036 speak_char(ch);1037}1038 1039static void say_position(struct vc_data *vc)1040{1041 synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,1042 vc->vc_num + 1);1043 synth_printf("\n");1044}1045 1046/* Added by brianb */1047static void say_char_num(struct vc_data *vc)1048{1049 u_char tmp;1050 u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);1051 1052 synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);1053}1054 1055/* these are stub functions to keep keyboard.c happy. */1056 1057static void say_from_top(struct vc_data *vc)1058{1059 say_screen_from_to(vc, 0, spk_y);1060}1061 1062static void say_to_bottom(struct vc_data *vc)1063{1064 say_screen_from_to(vc, spk_y, vc->vc_rows);1065}1066 1067static void say_from_left(struct vc_data *vc)1068{1069 say_line_from_to(vc, 0, spk_x, 1);1070}1071 1072static void say_to_right(struct vc_data *vc)1073{1074 say_line_from_to(vc, spk_x, vc->vc_cols, 1);1075}1076 1077/* end of stub functions. */1078 1079static void spkup_write(const u16 *in_buf, int count)1080{1081 static int rep_count;1082 static u16 ch = '\0', old_ch = '\0';1083 static u_short char_type, last_type;1084 int in_count = count;1085 1086 spk_keydown = 0;1087 while (count--) {1088 if (cursor_track == read_all_mode) {1089 /* Insert Sentence Index */1090 if ((in_buf == sentmarks[bn][currsentence]) &&1091 (currsentence <= numsentences[bn]))1092 synth_insert_next_index(currsentence++);1093 }1094 ch = *in_buf++;1095 if (ch < 0x100)1096 char_type = spk_chartab[ch];1097 else1098 char_type = ALPHA;1099 if (ch == old_ch && !(char_type & B_NUM)) {1100 if (++rep_count > 2)1101 continue;1102 } else {1103 if ((last_type & CH_RPT) && rep_count > 2) {1104 synth_printf(" ");1105 synth_printf(spk_msg_get(MSG_REPEAT_DESC),1106 ++rep_count);1107 synth_printf(" ");1108 }1109 rep_count = 0;1110 }1111 if (ch == spk_lastkey) {1112 rep_count = 0;1113 if (spk_key_echo == 1 && ch >= MINECHOCHAR)1114 speak_char(ch);1115 } else if (char_type & B_ALPHA) {1116 if ((synth_flags & SF_DEC) && (last_type & PUNC))1117 synth_buffer_add(SPACE);1118 synth_putwc_s(ch);1119 } else if (char_type & B_NUM) {1120 rep_count = 0;1121 synth_putwc_s(ch);1122 } else if (char_type & spk_punc_mask) {1123 speak_char(ch);1124 char_type &= ~PUNC; /* for dec nospell processing */1125 } else if (char_type & SYNTH_OK) {1126 /* these are usually puncts like . and , which synth1127 * needs for expression.1128 * suppress multiple to get rid of long pauses and1129 * clear repeat count1130 * so if someone has1131 * repeats on you don't get nothing repeated count1132 */1133 if (ch != old_ch)1134 synth_putwc_s(ch);1135 else1136 rep_count = 0;1137 } else {1138/* send space and record position, if next is num overwrite space */1139 if (old_ch != ch)1140 synth_buffer_add(SPACE);1141 else1142 rep_count = 0;1143 }1144 old_ch = ch;1145 last_type = char_type;1146 }1147 spk_lastkey = 0;1148 if (in_count > 2 && rep_count > 2) {1149 if (last_type & CH_RPT) {1150 synth_printf(" ");1151 synth_printf(spk_msg_get(MSG_REPEAT_DESC2),1152 ++rep_count);1153 synth_printf(" ");1154 }1155 rep_count = 0;1156 }1157}1158 1159static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);1160 1161static void read_all_doc(struct vc_data *vc);1162static void cursor_done(struct timer_list *unused);1163static DEFINE_TIMER(cursor_timer, cursor_done);1164 1165static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)1166{1167 unsigned long flags;1168 1169 if (!synth || up_flag || spk_killed)1170 return;1171 spin_lock_irqsave(&speakup_info.spinlock, flags);1172 if (cursor_track == read_all_mode) {1173 switch (value) {1174 case KVAL(K_SHIFT):1175 del_timer(&cursor_timer);1176 spk_shut_up &= 0xfe;1177 spk_do_flush();1178 read_all_doc(vc);1179 break;1180 case KVAL(K_CTRL):1181 del_timer(&cursor_timer);1182 cursor_track = prev_cursor_track;1183 spk_shut_up &= 0xfe;1184 spk_do_flush();1185 break;1186 }1187 } else {1188 spk_shut_up &= 0xfe;1189 spk_do_flush();1190 }1191 if (spk_say_ctrl && value < NUM_CTL_LABELS)1192 synth_printf("%s", spk_msg_get(MSG_CTL_START + value));1193 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1194}1195 1196static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)1197{1198 unsigned long flags;1199 1200 spin_lock_irqsave(&speakup_info.spinlock, flags);1201 if (up_flag) {1202 spk_lastkey = 0;1203 spk_keydown = 0;1204 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1205 return;1206 }1207 if (!synth || spk_killed) {1208 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1209 return;1210 }1211 spk_shut_up &= 0xfe;1212 spk_lastkey = value;1213 spk_keydown++;1214 spk_parked &= 0xfe;1215 if (spk_key_echo == 2 && value >= MINECHOCHAR)1216 speak_char(value);1217 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1218}1219 1220int spk_set_key_info(const u_char *key_info, u_char *k_buffer)1221{1222 int i = 0, states, key_data_len;1223 const u_char *cp = key_info;1224 u_char *cp1 = k_buffer;1225 u_char ch, version, num_keys;1226 1227 version = *cp++;1228 if (version != KEY_MAP_VER) {1229 pr_debug("version found %d should be %d\n",1230 version, KEY_MAP_VER);1231 return -EINVAL;1232 }1233 num_keys = *cp;1234 states = (int)cp[1];1235 key_data_len = (states + 1) * (num_keys + 1);1236 if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {1237 pr_debug("too many key_infos (%d over %u)\n",1238 key_data_len + SHIFT_TBL_SIZE + 4,1239 (unsigned int)(sizeof(spk_key_buf)));1240 return -EINVAL;1241 }1242 memset(k_buffer, 0, SHIFT_TBL_SIZE);1243 memset(spk_our_keys, 0, sizeof(spk_our_keys));1244 spk_shift_table = k_buffer;1245 spk_our_keys[0] = spk_shift_table;1246 cp1 += SHIFT_TBL_SIZE;1247 memcpy(cp1, cp, key_data_len + 3);1248 /* get num_keys, states and data */1249 cp1 += 2; /* now pointing at shift states */1250 for (i = 1; i <= states; i++) {1251 ch = *cp1++;1252 if (ch >= SHIFT_TBL_SIZE) {1253 pr_debug("(%d) not valid shift state (max_allowed = %d)\n",1254 ch, SHIFT_TBL_SIZE);1255 return -EINVAL;1256 }1257 spk_shift_table[ch] = i;1258 }1259 keymap_flags = *cp1++;1260 while ((ch = *cp1)) {1261 if (ch >= MAX_KEY) {1262 pr_debug("(%d), not valid key, (max_allowed = %d)\n",1263 ch, MAX_KEY);1264 return -EINVAL;1265 }1266 spk_our_keys[ch] = cp1;1267 cp1 += states + 1;1268 }1269 return 0;1270}1271 1272enum spk_vars_id {1273 BELL_POS_ID = 0, SPELL_DELAY_ID, ATTRIB_BLEEP_ID,1274 BLEEPS_ID, BLEEP_TIME_ID, PUNC_LEVEL_ID,1275 READING_PUNC_ID, CURSOR_TIME_ID, SAY_CONTROL_ID,1276 SAY_WORD_CTL_ID, NO_INTERRUPT_ID, KEY_ECHO_ID,1277 CUR_PHONETIC_ID, V_LAST_VAR_ID, NB_ID1278};1279 1280static struct var_t spk_vars[NB_ID] = {1281 /* bell must be first to set high limit */1282 [BELL_POS_ID] = { BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },1283 [SPELL_DELAY_ID] = { SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },1284 [ATTRIB_BLEEP_ID] = { ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },1285 [BLEEPS_ID] = { BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },1286 [BLEEP_TIME_ID] = { BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },1287 [PUNC_LEVEL_ID] = { PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },1288 [READING_PUNC_ID] = { READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },1289 [CURSOR_TIME_ID] = { CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },1290 [SAY_CONTROL_ID] = { SAY_CONTROL, TOGGLE_0},1291 [SAY_WORD_CTL_ID] = {SAY_WORD_CTL, TOGGLE_0},1292 [NO_INTERRUPT_ID] = { NO_INTERRUPT, TOGGLE_0},1293 [KEY_ECHO_ID] = { KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },1294 [CUR_PHONETIC_ID] = { CUR_PHONETIC, .u.n = {NULL, 0, 0, 1, 0, 0, NULL} },1295 V_LAST_VAR1296};1297 1298static void toggle_cursoring(struct vc_data *vc)1299{1300 if (cursor_track == read_all_mode)1301 cursor_track = prev_cursor_track;1302 if (++cursor_track >= CT_Max)1303 cursor_track = 0;1304 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));1305}1306 1307void spk_reset_default_chars(void)1308{1309 int i;1310 1311 /* First, free any non-default */1312 for (i = 0; i < 256; i++) {1313 if (spk_characters[i] &&1314 (spk_characters[i] != spk_default_chars[i]))1315 kfree(spk_characters[i]);1316 }1317 1318 memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));1319}1320 1321void spk_reset_default_chartab(void)1322{1323 memcpy(spk_chartab, default_chartab, sizeof(default_chartab));1324}1325 1326static const struct st_bits_data *pb_edit;1327 1328static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)1329{1330 short mask = pb_edit->mask, ch_type = spk_chartab[ch];1331 1332 if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)1333 return -1;1334 if (ch == SPACE) {1335 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));1336 spk_special_handler = NULL;1337 return 1;1338 }1339 if (mask < PUNC && !(ch_type & PUNC))1340 return -1;1341 spk_chartab[ch] ^= mask;1342 speak_char(ch);1343 synth_printf(" %s\n",1344 (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :1345 spk_msg_get(MSG_OFF));1346 return 1;1347}1348 1349/* Allocation concurrency is protected by the console semaphore */1350static int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags)1351{1352 int vc_num;1353 1354 vc_num = vc->vc_num;1355 if (!speakup_console[vc_num]) {1356 speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),1357 gfp_flags);1358 if (!speakup_console[vc_num])1359 return -ENOMEM;1360 speakup_date(vc);1361 } else if (!spk_parked) {1362 speakup_date(vc);1363 }1364 1365 return 0;1366}1367 1368static void speakup_deallocate(struct vc_data *vc)1369{1370 int vc_num;1371 1372 vc_num = vc->vc_num;1373 kfree(speakup_console[vc_num]);1374 speakup_console[vc_num] = NULL;1375}1376 1377enum read_all_command {1378 RA_NEXT_SENT = KVAL(K_DOWN)+1,1379 RA_PREV_LINE = KVAL(K_LEFT)+1,1380 RA_NEXT_LINE = KVAL(K_RIGHT)+1,1381 RA_PREV_SENT = KVAL(K_UP)+1,1382 RA_DOWN_ARROW,1383 RA_TIMER,1384 RA_FIND_NEXT_SENT,1385 RA_FIND_PREV_SENT,1386};1387 1388static u_char is_cursor;1389static u_long old_cursor_pos, old_cursor_x, old_cursor_y;1390static int cursor_con;1391 1392static void reset_highlight_buffers(struct vc_data *);1393 1394static enum read_all_command read_all_key;1395 1396static int in_keyboard_notifier;1397 1398static void start_read_all_timer(struct vc_data *vc, enum read_all_command command);1399 1400static void kbd_fakekey2(struct vc_data *vc, enum read_all_command command)1401{1402 del_timer(&cursor_timer);1403 speakup_fake_down_arrow();1404 start_read_all_timer(vc, command);1405}1406 1407static void read_all_doc(struct vc_data *vc)1408{1409 if ((vc->vc_num != fg_console) || !synth || spk_shut_up)1410 return;1411 if (!synth_supports_indexing())1412 return;1413 if (cursor_track != read_all_mode)1414 prev_cursor_track = cursor_track;1415 cursor_track = read_all_mode;1416 spk_reset_index_count(0);1417 if (get_sentence_buf(vc, 0) == -1) {1418 del_timer(&cursor_timer);1419 if (!in_keyboard_notifier)1420 speakup_fake_down_arrow();1421 start_read_all_timer(vc, RA_DOWN_ARROW);1422 } else {1423 say_sentence_num(0, 0);1424 synth_insert_next_index(0);1425 start_read_all_timer(vc, RA_TIMER);1426 }1427}1428 1429static void stop_read_all(struct vc_data *vc)1430{1431 del_timer(&cursor_timer);1432 cursor_track = prev_cursor_track;1433 spk_shut_up &= 0xfe;1434 spk_do_flush();1435}1436 1437static void start_read_all_timer(struct vc_data *vc, enum read_all_command command)1438{1439 struct var_t *cursor_timeout;1440 1441 cursor_con = vc->vc_num;1442 read_all_key = command;1443 cursor_timeout = spk_get_var(CURSOR_TIME);1444 mod_timer(&cursor_timer,1445 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));1446}1447 1448static void handle_cursor_read_all(struct vc_data *vc, enum read_all_command command)1449{1450 int indcount, sentcount, rv, sn;1451 1452 switch (command) {1453 case RA_NEXT_SENT:1454 /* Get Current Sentence */1455 spk_get_index_count(&indcount, &sentcount);1456 /*printk("%d %d ", indcount, sentcount); */1457 spk_reset_index_count(sentcount + 1);1458 if (indcount == 1) {1459 if (!say_sentence_num(sentcount + 1, 0)) {1460 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);1461 return;1462 }1463 synth_insert_next_index(0);1464 } else {1465 sn = 0;1466 if (!say_sentence_num(sentcount + 1, 1)) {1467 sn = 1;1468 spk_reset_index_count(sn);1469 } else {1470 synth_insert_next_index(0);1471 }1472 if (!say_sentence_num(sn, 0)) {1473 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);1474 return;1475 }1476 synth_insert_next_index(0);1477 }1478 start_read_all_timer(vc, RA_TIMER);1479 break;1480 case RA_PREV_SENT:1481 break;1482 case RA_NEXT_LINE:1483 read_all_doc(vc);1484 break;1485 case RA_PREV_LINE:1486 break;1487 case RA_DOWN_ARROW:1488 if (get_sentence_buf(vc, 0) == -1) {1489 kbd_fakekey2(vc, RA_DOWN_ARROW);1490 } else {1491 say_sentence_num(0, 0);1492 synth_insert_next_index(0);1493 start_read_all_timer(vc, RA_TIMER);1494 }1495 break;1496 case RA_FIND_NEXT_SENT:1497 rv = get_sentence_buf(vc, 0);1498 if (rv == -1)1499 read_all_doc(vc);1500 if (rv == 0) {1501 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);1502 } else {1503 say_sentence_num(1, 0);1504 synth_insert_next_index(0);1505 start_read_all_timer(vc, RA_TIMER);1506 }1507 break;1508 case RA_FIND_PREV_SENT:1509 break;1510 case RA_TIMER:1511 spk_get_index_count(&indcount, &sentcount);1512 if (indcount < 2)1513 kbd_fakekey2(vc, RA_DOWN_ARROW);1514 else1515 start_read_all_timer(vc, RA_TIMER);1516 break;1517 }1518}1519 1520static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)1521{1522 unsigned long flags;1523 1524 spin_lock_irqsave(&speakup_info.spinlock, flags);1525 if (cursor_track == read_all_mode) {1526 spk_parked &= 0xfe;1527 if (!synth || up_flag || spk_shut_up) {1528 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1529 return NOTIFY_STOP;1530 }1531 del_timer(&cursor_timer);1532 spk_shut_up &= 0xfe;1533 spk_do_flush();1534 start_read_all_timer(vc, value + 1);1535 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1536 return NOTIFY_STOP;1537 }1538 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1539 return NOTIFY_OK;1540}1541 1542static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)1543{1544 unsigned long flags;1545 struct var_t *cursor_timeout;1546 1547 spin_lock_irqsave(&speakup_info.spinlock, flags);1548 spk_parked &= 0xfe;1549 if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) {1550 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1551 return;1552 }1553 spk_shut_up &= 0xfe;1554 if (spk_no_intr)1555 spk_do_flush();1556/* the key press flushes if !no_inter but we want to flush on cursor1557 * moves regardless of no_inter state1558 */1559 is_cursor = value + 1;1560 old_cursor_pos = vc->vc_pos;1561 old_cursor_x = vc->state.x;1562 old_cursor_y = vc->state.y;1563 speakup_console[vc->vc_num]->ht.cy = vc->state.y;1564 cursor_con = vc->vc_num;1565 if (cursor_track == CT_Highlight)1566 reset_highlight_buffers(vc);1567 cursor_timeout = spk_get_var(CURSOR_TIME);1568 mod_timer(&cursor_timer,1569 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));1570 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1571}1572 1573static void update_color_buffer(struct vc_data *vc, const u16 *ic, int len)1574{1575 int i, bi, hi;1576 int vc_num = vc->vc_num;1577 1578 bi = (vc->vc_attr & 0x70) >> 4;1579 hi = speakup_console[vc_num]->ht.highsize[bi];1580 1581 i = 0;1582 if (speakup_console[vc_num]->ht.highsize[bi] == 0) {1583 speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;1584 speakup_console[vc_num]->ht.rx[bi] = vc->state.x;1585 speakup_console[vc_num]->ht.ry[bi] = vc->state.y;1586 }1587 while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {1588 if (ic[i] > 32) {1589 speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];1590 hi++;1591 } else if ((ic[i] == 32) && (hi != 0)) {1592 if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=1593 32) {1594 speakup_console[vc_num]->ht.highbuf[bi][hi] =1595 ic[i];1596 hi++;1597 }1598 }1599 i++;1600 }1601 speakup_console[vc_num]->ht.highsize[bi] = hi;1602}1603 1604static void reset_highlight_buffers(struct vc_data *vc)1605{1606 int i;1607 int vc_num = vc->vc_num;1608 1609 for (i = 0; i < 8; i++)1610 speakup_console[vc_num]->ht.highsize[i] = 0;1611}1612 1613static int count_highlight_color(struct vc_data *vc)1614{1615 int i, bg;1616 int cc;1617 int vc_num = vc->vc_num;1618 u16 ch;1619 u16 *start = (u16 *)vc->vc_origin;1620 1621 for (i = 0; i < 8; i++)1622 speakup_console[vc_num]->ht.bgcount[i] = 0;1623 1624 for (i = 0; i < vc->vc_rows; i++) {1625 u16 *end = start + vc->vc_cols * 2;1626 u16 *ptr;1627 1628 for (ptr = start; ptr < end; ptr++) {1629 ch = get_attributes(vc, ptr);1630 bg = (ch & 0x70) >> 4;1631 speakup_console[vc_num]->ht.bgcount[bg]++;1632 }1633 start += vc->vc_size_row;1634 }1635 1636 cc = 0;1637 for (i = 0; i < 8; i++)1638 if (speakup_console[vc_num]->ht.bgcount[i] > 0)1639 cc++;1640 return cc;1641}1642 1643static int get_highlight_color(struct vc_data *vc)1644{1645 int i, j;1646 unsigned int cptr[8];1647 int vc_num = vc->vc_num;1648 1649 for (i = 0; i < 8; i++)1650 cptr[i] = i;1651 1652 for (i = 0; i < 7; i++)1653 for (j = i + 1; j < 8; j++)1654 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >1655 speakup_console[vc_num]->ht.bgcount[cptr[j]])1656 swap(cptr[i], cptr[j]);1657 1658 for (i = 0; i < 8; i++)1659 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)1660 if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)1661 return cptr[i];1662 return -1;1663}1664 1665static int speak_highlight(struct vc_data *vc)1666{1667 int hc, d;1668 int vc_num = vc->vc_num;1669 1670 if (count_highlight_color(vc) == 1)1671 return 0;1672 hc = get_highlight_color(vc);1673 if (hc != -1) {1674 d = vc->state.y - speakup_console[vc_num]->ht.cy;1675 if ((d == 1) || (d == -1))1676 if (speakup_console[vc_num]->ht.ry[hc] != vc->state.y)1677 return 0;1678 spk_parked |= 0x01;1679 spk_do_flush();1680 spkup_write(speakup_console[vc_num]->ht.highbuf[hc],1681 speakup_console[vc_num]->ht.highsize[hc]);1682 spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];1683 spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];1684 spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];1685 return 1;1686 }1687 return 0;1688}1689 1690static void cursor_done(struct timer_list *unused)1691{1692 struct vc_data *vc = vc_cons[cursor_con].d;1693 unsigned long flags;1694 1695 del_timer(&cursor_timer);1696 spin_lock_irqsave(&speakup_info.spinlock, flags);1697 if (cursor_con != fg_console) {1698 is_cursor = 0;1699 goto out;1700 }1701 speakup_date(vc);1702 if (win_enabled) {1703 if (vc->state.x >= win_left && vc->state.x <= win_right &&1704 vc->state.y >= win_top && vc->state.y <= win_bottom) {1705 spk_keydown = 0;1706 is_cursor = 0;1707 goto out;1708 }1709 }1710 if (cursor_track == read_all_mode) {1711 handle_cursor_read_all(vc, read_all_key);1712 goto out;1713 }1714 if (cursor_track == CT_Highlight) {1715 if (speak_highlight(vc)) {1716 spk_keydown = 0;1717 is_cursor = 0;1718 goto out;1719 }1720 }1721 if (cursor_track == CT_Window)1722 speakup_win_say(vc);1723 else if (is_cursor == 1 || is_cursor == 4)1724 say_line_from_to(vc, 0, vc->vc_cols, 0);1725 else {1726 if (spk_cur_phonetic == 1)1727 say_phonetic_char(vc);1728 else1729 say_char(vc);1730 }1731 spk_keydown = 0;1732 is_cursor = 0;1733out:1734 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1735}1736 1737/* called by: vt_notifier_call() */1738static void speakup_bs(struct vc_data *vc)1739{1740 unsigned long flags;1741 1742 if (!speakup_console[vc->vc_num])1743 return;1744 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))1745 /* Speakup output, discard */1746 return;1747 if (!spk_parked)1748 speakup_date(vc);1749 if (spk_shut_up || !synth) {1750 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1751 return;1752 }1753 if (vc->vc_num == fg_console && spk_keydown) {1754 spk_keydown = 0;1755 if (!is_cursor)1756 say_char(vc);1757 }1758 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1759}1760 1761/* called by: vt_notifier_call() */1762static void speakup_con_write(struct vc_data *vc, u16 *str, int len)1763{1764 unsigned long flags;1765 1766 if ((vc->vc_num != fg_console) || spk_shut_up || !synth)1767 return;1768 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))1769 /* Speakup output, discard */1770 return;1771 if (spk_bell_pos && spk_keydown && (vc->state.x == spk_bell_pos - 1))1772 bleep(3);1773 if ((is_cursor) || (cursor_track == read_all_mode)) {1774 if (cursor_track == CT_Highlight)1775 update_color_buffer(vc, str, len);1776 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1777 return;1778 }1779 if (win_enabled) {1780 if (vc->state.x >= win_left && vc->state.x <= win_right &&1781 vc->state.y >= win_top && vc->state.y <= win_bottom) {1782 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1783 return;1784 }1785 }1786 1787 spkup_write(str, len);1788 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1789}1790 1791static void speakup_con_update(struct vc_data *vc)1792{1793 unsigned long flags;1794 1795 if (!speakup_console[vc->vc_num] || spk_parked || !synth)1796 return;1797 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))1798 /* Speakup output, discard */1799 return;1800 speakup_date(vc);1801 if (vc->vc_mode == KD_GRAPHICS && !spk_paused && spk_str_pause[0]) {1802 synth_printf("%s", spk_str_pause);1803 spk_paused = true;1804 }1805 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1806}1807 1808static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)1809{1810 unsigned long flags;1811 int on_off = 2;1812 char *label;1813 1814 if (!synth || up_flag || spk_killed)1815 return;1816 spin_lock_irqsave(&speakup_info.spinlock, flags);1817 spk_shut_up &= 0xfe;1818 if (spk_no_intr)1819 spk_do_flush();1820 switch (value) {1821 case KVAL(K_CAPS):1822 label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);1823 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);1824 break;1825 case KVAL(K_NUM):1826 label = spk_msg_get(MSG_KEYNAME_NUMLOCK);1827 on_off = vt_get_leds(fg_console, VC_NUMLOCK);1828 break;1829 case KVAL(K_HOLD):1830 label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);1831 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);1832 if (speakup_console[vc->vc_num])1833 speakup_console[vc->vc_num]->tty_stopped = on_off;1834 break;1835 default:1836 spk_parked &= 0xfe;1837 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1838 return;1839 }1840 if (on_off < 2)1841 synth_printf("%s %s\n",1842 label, spk_msg_get(MSG_STATUS_START + on_off));1843 spin_unlock_irqrestore(&speakup_info.spinlock, flags);1844}1845 1846static int inc_dec_var(u_char value)1847{1848 struct st_var_header *p_header;1849 struct var_t *var_data;1850 char num_buf[32];1851 char *cp = num_buf;1852 char *pn;1853 int var_id = (int)value - VAR_START;1854 int how = (var_id & 1) ? E_INC : E_DEC;1855 1856 var_id = var_id / 2 + FIRST_SET_VAR;1857 p_header = spk_get_var_header(var_id);1858 if (!p_header)1859 return -1;1860 if (p_header->var_type != VAR_NUM)1861 return -1;1862 var_data = p_header->data;1863 if (spk_set_num_var(1, p_header, how) != 0)1864 return -1;1865 if (!spk_close_press) {1866 for (pn = p_header->name; *pn; pn++) {1867 if (*pn == '_')1868 *cp = SPACE;1869 else1870 *cp++ = *pn;1871 }1872 }1873 snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",1874 var_data->u.n.value);1875 synth_printf("%s", num_buf);1876 return 0;1877}1878 1879static void speakup_win_set(struct vc_data *vc)1880{1881 char info[40];1882 1883 if (win_start > 1) {1884 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));1885 return;1886 }1887 if (spk_x < win_left || spk_y < win_top) {1888 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));1889 return;1890 }1891 if (win_start && spk_x == win_left && spk_y == win_top) {1892 win_left = 0;1893 win_right = vc->vc_cols - 1;1894 win_bottom = spk_y;1895 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),1896 (int)win_top + 1);1897 } else {1898 if (!win_start) {1899 win_top = spk_y;1900 win_left = spk_x;1901 } else {1902 win_bottom = spk_y;1903 win_right = spk_x;1904 }1905 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),1906 (win_start) ?1907 spk_msg_get(MSG_END) : spk_msg_get(MSG_START),1908 (int)spk_y + 1, (int)spk_x + 1);1909 }1910 synth_printf("%s\n", info);1911 win_start++;1912}1913 1914static void speakup_win_clear(struct vc_data *vc)1915{1916 win_top = 0;1917 win_bottom = 0;1918 win_left = 0;1919 win_right = 0;1920 win_start = 0;1921 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));1922}1923 1924static void speakup_win_enable(struct vc_data *vc)1925{1926 if (win_start < 2) {1927 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));1928 return;1929 }1930 win_enabled ^= 1;1931 if (win_enabled)1932 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));1933 else1934 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));1935}1936 1937static void speakup_bits(struct vc_data *vc)1938{1939 int val = this_speakup_key - (FIRST_EDIT_BITS - 1);1940 1941 if (spk_special_handler || val < 1 || val > 6) {1942 synth_printf("%s\n", spk_msg_get(MSG_ERROR));1943 return;1944 }1945 pb_edit = &spk_punc_info[val];1946 synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);1947 spk_special_handler = edit_bits;1948}1949 1950static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)1951{1952 static u_char goto_buf[8];1953 static int num;1954 int maxlen;1955 char *cp;1956 u16 wch;1957 1958 if (type == KT_SPKUP && ch == SPEAKUP_GOTO)1959 goto do_goto;1960 if (type == KT_LATIN && ch == '\n')1961 goto do_goto;1962 if (type != 0)1963 goto oops;1964 if (ch == 8) {1965 u16 wch;1966 1967 if (num == 0)1968 return -1;1969 wch = goto_buf[--num];1970 goto_buf[num] = '\0';1971 spkup_write(&wch, 1);1972 return 1;1973 }1974 if (ch < '+' || ch > 'y')1975 goto oops;1976 wch = ch;1977 goto_buf[num++] = ch;1978 goto_buf[num] = '\0';1979 spkup_write(&wch, 1);1980 maxlen = (*goto_buf >= '0') ? 3 : 4;1981 if ((ch == '+' || ch == '-') && num == 1)1982 return 1;1983 if (ch >= '0' && ch <= '9' && num < maxlen)1984 return 1;1985 if (num < maxlen - 1 || num > maxlen)1986 goto oops;1987 if (ch < 'x' || ch > 'y') {1988oops:1989 if (!spk_killed)1990 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));1991 goto_buf[num = 0] = '\0';1992 spk_special_handler = NULL;1993 return 1;1994 }1995 1996 /* Do not replace with kstrtoul: here we need cp to be updated */1997 goto_pos = simple_strtoul(goto_buf, &cp, 10);1998 1999 if (*cp == 'x') {2000 if (*goto_buf < '0')2001 goto_pos += spk_x;2002 else if (goto_pos > 0)2003 goto_pos--;2004 2005 if (goto_pos >= vc->vc_cols)2006 goto_pos = vc->vc_cols - 1;2007 goto_x = 1;2008 } else {2009 if (*goto_buf < '0')2010 goto_pos += spk_y;2011 else if (goto_pos > 0)2012 goto_pos--;2013 2014 if (goto_pos >= vc->vc_rows)2015 goto_pos = vc->vc_rows - 1;2016 goto_x = 0;2017 }2018 goto_buf[num = 0] = '\0';2019do_goto:2020 spk_special_handler = NULL;2021 spk_parked |= 0x01;2022 if (goto_x) {2023 spk_pos -= spk_x * 2;2024 spk_x = goto_pos;2025 spk_pos += goto_pos * 2;2026 say_word(vc);2027 } else {2028 spk_y = goto_pos;2029 spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);2030 say_line(vc);2031 }2032 return 1;2033}2034 2035static void speakup_goto(struct vc_data *vc)2036{2037 if (spk_special_handler) {2038 synth_printf("%s\n", spk_msg_get(MSG_ERROR));2039 return;2040 }2041 synth_printf("%s\n", spk_msg_get(MSG_GOTO));2042 spk_special_handler = handle_goto;2043}2044 2045static void speakup_help(struct vc_data *vc)2046{2047 spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);2048}2049 2050static void do_nothing(struct vc_data *vc)2051{2052 return; /* flush done in do_spkup */2053}2054 2055static u_char key_speakup, spk_key_locked;2056 2057static void speakup_lock(struct vc_data *vc)2058{2059 if (!spk_key_locked) {2060 spk_key_locked = 16;2061 key_speakup = 16;2062 } else {2063 spk_key_locked = 0;2064 key_speakup = 0;2065 }2066}2067 2068typedef void (*spkup_hand) (struct vc_data *);2069static spkup_hand spkup_handler[] = {2070 /* must be ordered same as defines in speakup.h */2071 do_nothing, speakup_goto, speech_kill, speakup_shut_up,2072 speakup_cut, speakup_paste, say_first_char, say_last_char,2073 say_char, say_prev_char, say_next_char,2074 say_word, say_prev_word, say_next_word,2075 say_line, say_prev_line, say_next_line,2076 top_edge, bottom_edge, left_edge, right_edge,2077 spell_word, spell_word, say_screen,2078 say_position, say_attributes,2079 speakup_off, speakup_parked, say_line, /* this is for indent */2080 say_from_top, say_to_bottom,2081 say_from_left, say_to_right,2082 say_char_num, speakup_bits, speakup_bits, say_phonetic_char,2083 speakup_bits, speakup_bits, speakup_bits,2084 speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,2085 speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL2086};2087 2088static void do_spkup(struct vc_data *vc, u_char value)2089{2090 if (spk_killed && value != SPEECH_KILL)2091 return;2092 spk_keydown = 0;2093 spk_lastkey = 0;2094 spk_shut_up &= 0xfe;2095 this_speakup_key = value;2096 if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {2097 spk_do_flush();2098 (*spkup_handler[value]) (vc);2099 } else {2100 if (inc_dec_var(value) < 0)2101 bleep(9);2102 }2103}2104 2105static const char *pad_chars = "0123456789+-*/\015,.?()";2106 2107static int2108speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,2109 int up_flag)2110{2111 unsigned long flags;2112 int kh;2113 u_char *key_info;2114 u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;2115 u_char shift_info, offset;2116 int ret = 0;2117 2118 if (!synth)2119 return 0;2120 2121 spin_lock_irqsave(&speakup_info.spinlock, flags);2122 tty = vc->port.tty;2123 if (type >= 0xf0)2124 type -= 0xf0;2125 if (type == KT_PAD &&2126 (vt_get_leds(fg_console, VC_NUMLOCK))) {2127 if (up_flag) {2128 spk_keydown = 0;2129 goto out;2130 }2131 value = pad_chars[value];2132 spk_lastkey = value;2133 spk_keydown++;2134 spk_parked &= 0xfe;2135 goto no_map;2136 }2137 if (keycode >= MAX_KEY)2138 goto no_map;2139 key_info = spk_our_keys[keycode];2140 if (!key_info)2141 goto no_map;2142 /* Check valid read all mode keys */2143 if ((cursor_track == read_all_mode) && (!up_flag)) {2144 switch (value) {2145 case KVAL(K_DOWN):2146 case KVAL(K_UP):2147 case KVAL(K_LEFT):2148 case KVAL(K_RIGHT):2149 case KVAL(K_PGUP):2150 case KVAL(K_PGDN):2151 break;2152 default:2153 stop_read_all(vc);2154 break;2155 }2156 }2157 shift_info = (shift_state & 0x0f) + key_speakup;2158 offset = spk_shift_table[shift_info];2159 if (offset) {2160 new_key = key_info[offset];2161 if (new_key) {2162 ret = 1;2163 if (new_key == SPK_KEY) {2164 if (!spk_key_locked)2165 key_speakup = (up_flag) ? 0 : 16;2166 if (up_flag || spk_killed)2167 goto out;2168 spk_shut_up &= 0xfe;2169 spk_do_flush();2170 goto out;2171 }2172 if (up_flag)2173 goto out;2174 if (last_keycode == keycode &&2175 time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {2176 spk_close_press = 1;2177 offset = spk_shift_table[shift_info + 32];2178 /* double press? */2179 if (offset && key_info[offset])2180 new_key = key_info[offset];2181 }2182 last_keycode = keycode;2183 last_spk_jiffy = jiffies;2184 type = KT_SPKUP;2185 value = new_key;2186 }2187 }2188no_map:2189 if (type == KT_SPKUP && !spk_special_handler) {2190 do_spkup(vc, new_key);2191 spk_close_press = 0;2192 ret = 1;2193 goto out;2194 }2195 if (up_flag || spk_killed || type == KT_SHIFT)2196 goto out;2197 spk_shut_up &= 0xfe;2198 kh = (value == KVAL(K_DOWN)) ||2199 (value == KVAL(K_UP)) ||2200 (value == KVAL(K_LEFT)) ||2201 (value == KVAL(K_RIGHT));2202 if ((cursor_track != read_all_mode) || !kh)2203 if (!spk_no_intr)2204 spk_do_flush();2205 if (spk_special_handler) {2206 if (type == KT_SPEC && value == 1) {2207 value = '\n';2208 type = KT_LATIN;2209 } else if (type == KT_LETTER) {2210 type = KT_LATIN;2211 } else if (value == 0x7f) {2212 value = 8; /* make del = backspace */2213 }2214 ret = (*spk_special_handler) (vc, type, value, keycode);2215 spk_close_press = 0;2216 if (ret < 0)2217 bleep(9);2218 goto out;2219 }2220 last_keycode = 0;2221out:2222 spin_unlock_irqrestore(&speakup_info.spinlock, flags);2223 return ret;2224}2225 2226static int keyboard_notifier_call(struct notifier_block *nb,2227 unsigned long code, void *_param)2228{2229 struct keyboard_notifier_param *param = _param;2230 struct vc_data *vc = param->vc;2231 int up = !param->down;2232 int ret = NOTIFY_OK;2233 static int keycode; /* to hold the current keycode */2234 2235 in_keyboard_notifier = 1;2236 2237 if (vc->vc_mode == KD_GRAPHICS)2238 goto out;2239 2240 /*2241 * First, determine whether we are handling a fake keypress on2242 * the current processor. If we are, then return NOTIFY_OK,2243 * to pass the keystroke up the chain. This prevents us from2244 * trying to take the Speakup lock while it is held by the2245 * processor on which the simulated keystroke was generated.2246 * Also, the simulated keystrokes should be ignored by Speakup.2247 */2248 2249 if (speakup_fake_key_pressed())2250 goto out;2251 2252 switch (code) {2253 case KBD_KEYCODE:2254 /* speakup requires keycode and keysym currently */2255 keycode = param->value;2256 break;2257 case KBD_UNBOUND_KEYCODE:2258 /* not used yet */2259 break;2260 case KBD_UNICODE:2261 /* not used yet */2262 break;2263 case KBD_KEYSYM:2264 if (speakup_key(vc, param->shift, keycode, param->value, up))2265 ret = NOTIFY_STOP;2266 else if (KTYP(param->value) == KT_CUR)2267 ret = pre_handle_cursor(vc, KVAL(param->value), up);2268 break;2269 case KBD_POST_KEYSYM:{2270 unsigned char type = KTYP(param->value) - 0xf0;2271 unsigned char val = KVAL(param->value);2272 2273 switch (type) {2274 case KT_SHIFT:2275 do_handle_shift(vc, val, up);2276 break;2277 case KT_LATIN:2278 case KT_LETTER:2279 do_handle_latin(vc, val, up);2280 break;2281 case KT_CUR:2282 do_handle_cursor(vc, val, up);2283 break;2284 case KT_SPEC:2285 do_handle_spec(vc, val, up);2286 break;2287 }2288 break;2289 }2290 }2291out:2292 in_keyboard_notifier = 0;2293 return ret;2294}2295 2296static int vt_notifier_call(struct notifier_block *nb,2297 unsigned long code, void *_param)2298{2299 struct vt_notifier_param *param = _param;2300 struct vc_data *vc = param->vc;2301 2302 switch (code) {2303 case VT_ALLOCATE:2304 if (vc->vc_mode == KD_TEXT)2305 speakup_allocate(vc, GFP_ATOMIC);2306 break;2307 case VT_DEALLOCATE:2308 speakup_deallocate(vc);2309 break;2310 case VT_WRITE:2311 if (param->c == '\b') {2312 speakup_bs(vc);2313 } else {2314 u16 d = param->c;2315 2316 speakup_con_write(vc, &d, 1);2317 }2318 break;2319 case VT_UPDATE:2320 speakup_con_update(vc);2321 break;2322 }2323 return NOTIFY_OK;2324}2325 2326/* called by: module_exit() */2327static void __exit speakup_exit(void)2328{2329 int i;2330 2331 unregister_keyboard_notifier(&keyboard_notifier_block);2332 unregister_vt_notifier(&vt_notifier_block);2333 speakup_unregister_devsynth();2334 speakup_cancel_selection();2335 speakup_cancel_paste();2336 del_timer_sync(&cursor_timer);2337 kthread_stop(speakup_task);2338 speakup_task = NULL;2339 mutex_lock(&spk_mutex);2340 synth_release();2341 mutex_unlock(&spk_mutex);2342 spk_ttyio_unregister_ldisc();2343 2344 speakup_kobj_exit();2345 2346 for (i = 0; i < MAX_NR_CONSOLES; i++)2347 kfree(speakup_console[i]);2348 2349 speakup_remove_virtual_keyboard();2350 2351 for (i = 0; i < MAXVARS; i++)2352 speakup_unregister_var(i);2353 2354 for (i = 0; i < 256; i++) {2355 if (spk_characters[i] != spk_default_chars[i])2356 kfree(spk_characters[i]);2357 }2358 2359 spk_free_user_msgs();2360}2361 2362/* call by: module_init() */2363static int __init speakup_init(void)2364{2365 int i;2366 long err = 0;2367 struct vc_data *vc = vc_cons[fg_console].d;2368 struct var_t *var;2369 2370 /* These first few initializations cannot fail. */2371 spk_initialize_msgs(); /* Initialize arrays for i18n. */2372 spk_reset_default_chars();2373 spk_reset_default_chartab();2374 spk_strlwr(synth_name);2375 spk_vars[0].u.n.high = vc->vc_cols;2376 for (var = spk_vars; var->var_id != MAXVARS; var++)2377 speakup_register_var(var);2378 for (var = synth_time_vars;2379 (var->var_id >= 0) && (var->var_id < MAXVARS); var++)2380 speakup_register_var(var);2381 for (i = 1; spk_punc_info[i].mask != 0; i++)2382 spk_set_mask_bits(NULL, i, 2);2383 2384 spk_set_key_info(spk_key_defaults, spk_key_buf);2385 2386 /* From here on out, initializations can fail. */2387 err = speakup_add_virtual_keyboard();2388 if (err)2389 goto error_virtkeyboard;2390 2391 for (i = 0; i < MAX_NR_CONSOLES; i++)2392 if (vc_cons[i].d) {2393 err = speakup_allocate(vc_cons[i].d, GFP_KERNEL);2394 if (err)2395 goto error_kobjects;2396 }2397 2398 if (spk_quiet_boot)2399 spk_shut_up |= 0x01;2400 2401 err = speakup_kobj_init();2402 if (err)2403 goto error_kobjects;2404 2405 spk_ttyio_register_ldisc();2406 synth_init(synth_name);2407 speakup_register_devsynth();2408 /*2409 * register_devsynth might fail, but this error is not fatal.2410 * /dev/synth is an extra feature; the rest of Speakup2411 * will work fine without it.2412 */2413 2414 err = register_keyboard_notifier(&keyboard_notifier_block);2415 if (err)2416 goto error_kbdnotifier;2417 err = register_vt_notifier(&vt_notifier_block);2418 if (err)2419 goto error_vtnotifier;2420 2421 speakup_task = kthread_create(speakup_thread, NULL, "speakup");2422 2423 if (IS_ERR(speakup_task)) {2424 err = PTR_ERR(speakup_task);2425 goto error_task;2426 }2427 2428 set_user_nice(speakup_task, 10);2429 wake_up_process(speakup_task);2430 2431 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);2432 pr_info("synth name on entry is: %s\n", synth_name);2433 goto out;2434 2435error_task:2436 unregister_vt_notifier(&vt_notifier_block);2437 2438error_vtnotifier:2439 unregister_keyboard_notifier(&keyboard_notifier_block);2440 del_timer(&cursor_timer);2441 2442error_kbdnotifier:2443 speakup_unregister_devsynth();2444 mutex_lock(&spk_mutex);2445 synth_release();2446 mutex_unlock(&spk_mutex);2447 speakup_kobj_exit();2448 2449error_kobjects:2450 for (i = 0; i < MAX_NR_CONSOLES; i++)2451 kfree(speakup_console[i]);2452 2453 speakup_remove_virtual_keyboard();2454 2455error_virtkeyboard:2456 for (i = 0; i < MAXVARS; i++)2457 speakup_unregister_var(i);2458 2459 for (i = 0; i < 256; i++) {2460 if (spk_characters[i] != spk_default_chars[i])2461 kfree(spk_characters[i]);2462 }2463 2464 spk_free_user_msgs();2465 2466out:2467 return err;2468}2469 2470module_param_named(bell_pos, spk_vars[BELL_POS_ID].u.n.default_val, int, 0444);2471module_param_named(spell_delay, spk_vars[SPELL_DELAY_ID].u.n.default_val, int, 0444);2472module_param_named(attrib_bleep, spk_vars[ATTRIB_BLEEP_ID].u.n.default_val, int, 0444);2473module_param_named(bleeps, spk_vars[BLEEPS_ID].u.n.default_val, int, 0444);2474module_param_named(bleep_time, spk_vars[BLEEP_TIME_ID].u.n.default_val, int, 0444);2475module_param_named(punc_level, spk_vars[PUNC_LEVEL_ID].u.n.default_val, int, 0444);2476module_param_named(reading_punc, spk_vars[READING_PUNC_ID].u.n.default_val, int, 0444);2477module_param_named(cursor_time, spk_vars[CURSOR_TIME_ID].u.n.default_val, int, 0444);2478module_param_named(say_control, spk_vars[SAY_CONTROL_ID].u.n.default_val, int, 0444);2479module_param_named(say_word_ctl, spk_vars[SAY_WORD_CTL_ID].u.n.default_val, int, 0444);2480module_param_named(no_interrupt, spk_vars[NO_INTERRUPT_ID].u.n.default_val, int, 0444);2481module_param_named(key_echo, spk_vars[KEY_ECHO_ID].u.n.default_val, int, 0444);2482module_param_named(cur_phonetic, spk_vars[CUR_PHONETIC_ID].u.n.default_val, int, 0444);2483 2484MODULE_PARM_DESC(bell_pos, "This works much like a typewriter bell. If for example 72 is echoed to bell_pos, it will beep the PC speaker when typing on a line past character 72.");2485MODULE_PARM_DESC(spell_delay, "This controls how fast a word is spelled when speakup's spell word review command is pressed.");2486MODULE_PARM_DESC(attrib_bleep, "Beeps the PC speaker when there is an attribute change such as background color when using speakup review commands. One = on, zero = off.");2487MODULE_PARM_DESC(bleeps, "This controls whether one hears beeps through the PC speaker when using speakup review commands.");2488MODULE_PARM_DESC(bleep_time, "This controls the duration of the PC speaker beeps speakup produces.");2489MODULE_PARM_DESC(punc_level, "Controls the level of punctuation spoken as the screen is displayed, not reviewed.");2490MODULE_PARM_DESC(reading_punc, "It controls the level of punctuation when reviewing the screen with speakup's screen review commands.");2491MODULE_PARM_DESC(cursor_time, "This controls cursor delay when using arrow keys.");2492MODULE_PARM_DESC(say_control, "This controls if speakup speaks shift, alt and control when those keys are pressed or not.");2493MODULE_PARM_DESC(say_word_ctl, "Sets the say_word_ctl on load.");2494MODULE_PARM_DESC(no_interrupt, "Controls if typing interrupts output from speakup.");2495MODULE_PARM_DESC(key_echo, "Controls if speakup speaks keys when they are typed. One = on zero = off or don't echo keys.");2496MODULE_PARM_DESC(cur_phonetic, "Controls if speakup speaks letters phonetically during navigation. One = on zero = off or don't speak phonetically.");2497 2498module_init(speakup_init);2499module_exit(speakup_exit);2500