2500 lines · 62.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* speakup.c * review functions for the speakup screen review package. * originally written by: Kirk Reiser and Andy Berdan. * * extensively modified by David Borowski. * ** Copyright (C) 1998  Kirk Reiser. *  Copyright (C) 2003  David Borowski. */ #include <linux/kernel.h>#include <linux/vt.h>#include <linux/tty.h>#include <linux/mm.h>		/* __get_free_page() and friends */#include <linux/vt_kern.h>#include <linux/ctype.h>#include <linux/selection.h>#include <linux/unistd.h>#include <linux/jiffies.h>#include <linux/kthread.h>#include <linux/keyboard.h>	/* for KT_SHIFT */#include <linux/kbd_kern.h>	/* for vc_kbd_* and friends */#include <linux/input.h>#include <linux/kmod.h> /* speakup_*_selection */#include <linux/module.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/consolemap.h> #include <linux/spinlock.h>#include <linux/notifier.h> #include <linux/uaccess.h>	/* copy_from|to|user() and others */ #include "spk_priv.h"#include "speakup.h" #define MAX_DELAY msecs_to_jiffies(500)#define MINECHOCHAR SPACE MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");MODULE_DESCRIPTION("Speakup console speech");MODULE_LICENSE("GPL");MODULE_VERSION(SPEAKUP_VERSION); char *synth_name;module_param_named(synth, synth_name, charp, 0444);module_param_named(quiet, spk_quiet_boot, bool, 0444); MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found."); special_func spk_special_handler; short spk_pitch_shift, synth_flags;static u16 buf[256];int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;int spk_no_intr, spk_spell_delay;int spk_key_echo, spk_say_word_ctl;int spk_say_ctrl, spk_bell_pos;u16 spk_punc_mask;int spk_punc_level, spk_reading_punc;int spk_cur_phonetic;char spk_str_caps_start[MAXVARLEN + 1] = "\0";char spk_str_caps_stop[MAXVARLEN + 1] = "\0";char spk_str_pause[MAXVARLEN + 1] = "\0";bool spk_paused;const struct st_bits_data spk_punc_info[] = {	{"none", "", 0},	{"some", "/$%&@", SOME},	{"most", "$%&#()=+*/@^<>|\\", MOST},	{"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC},	{"delimiters", "", B_WDLM},	{"repeats", "()", CH_RPT},	{"extended numeric", "", B_EXNUM},	{"symbols", "", B_SYM},	{NULL, NULL}}; static char mark_cut_flag;#define MAX_KEY 160static u_char *spk_shift_table;u_char *spk_our_keys[MAX_KEY];u_char spk_key_buf[600];const u_char spk_key_defaults[] = {#include "speakupmap.h"}; /* cursor track modes, must be ordered same as cursor_msgs in enum msg_index_t */enum cursor_track {	CT_Off = 0,	CT_On,	CT_Highlight,	CT_Window,	CT_Max,	read_all_mode = CT_Max,}; /* Speakup Cursor Track Variables */static enum cursor_track cursor_track = 1, prev_cursor_track = 1; static struct tty_struct *tty; static void spkup_write(const u16 *in_buf, int count); static char *phonetic[] = {	"alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",	"india", "juliett", "keelo", "leema", "mike", "november", "oscar",	    "papa",	"keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",	"x ray", "yankee", "zulu"}; /* array of 256 char pointers (one for each character description) * initialized to default_chars and user selectable via * /proc/speakup/characters */char *spk_characters[256]; char *spk_default_chars[256] = {/*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",/*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",/*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",/*024*/ "^x", "^y", "^z", "control", "control", "control", "control",	    "control",/*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",	    "tick",/*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",	    "dot",	"slash",/*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",	"eight", "nine",/*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",/*065*/ "EIGH", "B", "C", "D", "E", "F", "G",/*072*/ "H", "I", "J", "K", "L", "M", "N", "O",/*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",/*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",	    "caret",	"line",/*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",/*104*/ "h", "i", "j", "k", "l", "m", "n", "o",/*112*/ "p", "q", "r", "s", "t", "u", "v", "w",/*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",/*127*/ "del", "control", "control", "control", "control", "control",	    "control", "control", "control", "control", "control",/*138*/ "control", "control", "control", "control", "control",	    "control", "control", "control", "control", "control",	    "control", "control",/*150*/ "control", "control", "control", "control", "control",	    "control", "control", "control", "control", "control",/*160*/ "nbsp", "inverted bang",/*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",/*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",/*172*/ "not", "soft hyphen", "registered", "macron",/*176*/ "degrees", "plus or minus", "super two", "super three",/*180*/ "acute accent", "micro", "pilcrow", "middle dot",/*184*/ "cedilla", "super one", "male ordinal", "double right angle",/*188*/ "one quarter", "one half", "three quarters",	    "inverted question",/*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",	    "A RING",/*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",	    "E OOMLAUT",/*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",	    "N TILDE",/*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",/*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",	    "U CIRCUMFLEX",/*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",/*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",/*230*/ "ae", "c cidella", "e grave", "e acute",/*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",	    "i circumflex",/*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",	    "o circumflex",/*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",	    "u acute",/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"}; /* array of 256 u16 (one for each character) * initialized to default_chartab and user selectable via * /sys/module/speakup/parameters/chartab */u16 spk_chartab[256]; static u16 default_chartab[256] = {	B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/* 0-7 */	B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/* 8-15 */	B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/*16-23 */	B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/* 24-31 */	WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC,	/*  !"#$%&' */	PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC,	/* ()*+, -./ */	NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM,	/* 01234567 */	NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC,	/* 89:;<=>? */	PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* @ABCDEFG */	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* HIJKLMNO */	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* PQRSTUVW */	A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC,	/* XYZ[\]^_ */	PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* `abcdefg */	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* hijklmno */	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* pqrstuvw */	ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0,	/* xyz{|}~ */	B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */	B_SYM,	/* 135 */	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */	B_CAPSYM,	/* 143 */	B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */	B_SYM,	/* 151 */	B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */	B_SYM,	/* 159 */	WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */	B_SYM,	/* 167 */	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,	/* 168-175 */	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,	/* 176-183 */	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,	/* 184-191 */	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* 192-199 */	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* 200-207 */	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM,	/* 208-215 */	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA,	/* 216-223 */	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* 224-231 */	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* 232-239 */	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM,	/* 240-247 */	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA	/* 248-255 */}; struct task_struct *speakup_task;struct bleep spk_unprocessed_sound;static int spk_keydown;static u16 spk_lastkey;static u_char spk_close_press, keymap_flags;static u_char last_keycode, this_speakup_key;static u_long last_spk_jiffy; struct st_spk_t *speakup_console[MAX_NR_CONSOLES]; DEFINE_MUTEX(spk_mutex); static int keyboard_notifier_call(struct notifier_block *,				  unsigned long code, void *param); static struct notifier_block keyboard_notifier_block = {	.notifier_call = keyboard_notifier_call,}; static int vt_notifier_call(struct notifier_block *,			    unsigned long code, void *param); static struct notifier_block vt_notifier_block = {	.notifier_call = vt_notifier_call,}; static unsigned char get_attributes(struct vc_data *vc, u16 *pos){	pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);	return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8;} static void speakup_date(struct vc_data *vc){	spk_x = spk_cx = vc->state.x;	spk_y = spk_cy = vc->state.y;	spk_pos = spk_cp = vc->vc_pos;	spk_old_attr = spk_attr;	spk_attr = get_attributes(vc, (u16 *)spk_pos);} static void bleep(u16 val){	static const short vals[] = {		350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659	};	short freq;	int time = spk_bleep_time; 	freq = vals[val % 12];	if (val > 11)		freq *= (1 << (val / 12));	spk_unprocessed_sound.freq = freq;	spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);	spk_unprocessed_sound.active = 1;	/* We can only have 1 active sound at a time. */} static void speakup_shut_up(struct vc_data *vc){	if (spk_killed)		return;	spk_shut_up |= 0x01;	spk_parked &= 0xfe;	speakup_date(vc);	if (synth)		spk_do_flush();} static void speech_kill(struct vc_data *vc){	char val = synth->is_alive(synth); 	if (val == 0)		return; 	/* re-enables synth, if disabled */	if (val == 2 || spk_killed) {		/* dead */		spk_shut_up &= ~0x40;		synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));	} else {		synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));		spk_shut_up |= 0x40;	}} static void speakup_off(struct vc_data *vc){	if (spk_shut_up & 0x80) {		spk_shut_up &= 0x7f;		synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));	} else {		spk_shut_up |= 0x80;		synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));	}	speakup_date(vc);} static void speakup_parked(struct vc_data *vc){	if (spk_parked & 0x80) {		spk_parked = 0;		synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));	} else {		spk_parked |= 0x80;		synth_printf("%s\n", spk_msg_get(MSG_PARKED));	}} static void speakup_cut(struct vc_data *vc){	static const char err_buf[] = "set selection failed";	int ret; 	if (!mark_cut_flag) {		mark_cut_flag = 1;		spk_xs = (u16)spk_x;		spk_ys = (u16)spk_y;		spk_sel_cons = vc;		synth_printf("%s\n", spk_msg_get(MSG_MARK));		return;	}	spk_xe = (u16)spk_x;	spk_ye = (u16)spk_y;	mark_cut_flag = 0;	synth_printf("%s\n", spk_msg_get(MSG_CUT)); 	ret = speakup_set_selection(tty); 	switch (ret) {	case 0:		break;		/* no error */	case -EFAULT:		pr_warn("%sEFAULT\n", err_buf);		break;	case -EINVAL:		pr_warn("%sEINVAL\n", err_buf);		break;	case -ENOMEM:		pr_warn("%sENOMEM\n", err_buf);		break;	}} static void speakup_paste(struct vc_data *vc){	if (mark_cut_flag) {		mark_cut_flag = 0;		synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));	} else {		synth_printf("%s\n", spk_msg_get(MSG_PASTE));		speakup_paste_selection(tty);	}} static void say_attributes(struct vc_data *vc){	int fg = spk_attr & 0x0f;	int bg = spk_attr >> 4; 	synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));	if (bg > 7) {		synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));		bg -= 8;	} else {		synth_printf(" %s ", spk_msg_get(MSG_ON));	}	synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));} /* must be ordered same as edge_msgs in enum msg_index_t */enum edge {	edge_none = 0,	edge_top,	edge_bottom,	edge_left,	edge_right,	edge_quiet}; static void announce_edge(struct vc_data *vc, enum edge msg_id){	if (spk_bleeps & 1)		bleep(spk_y);	if ((spk_bleeps & 2) && (msg_id < edge_quiet))		synth_printf("%s\n",			     spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));} static void speak_char(u16 ch){	char *cp;	struct var_t *direct = spk_get_var(DIRECT); 	if (ch >= 0x100 || (direct && direct->u.n.value)) {		if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {			spk_pitch_shift++;			synth_printf("%s", spk_str_caps_start);		}		synth_putwc_s(ch);		if (ch < 0x100 && IS_CHAR(ch, B_CAP))			synth_printf("%s", spk_str_caps_stop);		return;	} 	cp = spk_characters[ch];	if (!cp) {		pr_info("%s: cp == NULL!\n", __func__);		return;	}	if (IS_CHAR(ch, B_CAP)) {		spk_pitch_shift++;		synth_printf("%s %s %s",			     spk_str_caps_start, cp, spk_str_caps_stop);	} else {		if (*cp == '^') {			cp++;			synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp);		} else {			synth_printf(" %s ", cp);		}	}} static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs){	u16 ch = ' '; 	if (vc && pos) {		u16 w;		u16 c; 		pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);		w = scr_readw(pos);		c = w & 0xff; 		if (w & vc->vc_hi_font_mask) {			w &= ~vc->vc_hi_font_mask;			c |= 0x100;		} 		ch = inverse_translate(vc, c, true);		*attribs = (w & 0xff00) >> 8;	}	return ch;} static void say_char(struct vc_data *vc){	u16 ch; 	spk_old_attr = spk_attr;	ch = get_char(vc, (u16 *)spk_pos, &spk_attr);	if (spk_attr != spk_old_attr) {		if (spk_attrib_bleep & 1)			bleep(spk_y);		if (spk_attrib_bleep & 2)			say_attributes(vc);	}	speak_char(ch);} static void say_phonetic_char(struct vc_data *vc){	u16 ch; 	spk_old_attr = spk_attr;	ch = get_char(vc, (u16 *)spk_pos, &spk_attr);

Lines 1–500 of 2500.