Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.9.4.
  1#include <linux/slab.h> /* for kmalloc */
  2#include <linux/consolemap.h>
  3#include <linux/interrupt.h>
  4#include <linux/sched.h>
  5#include <linux/selection.h>
  6
  7#include "speakup.h"
  8
  9/* ------ cut and paste ----- */
 10/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
 11#define ishardspace(c)      ((c) == ' ')
 12
 13unsigned short xs, ys, xe, ye; /* our region points */
 14
 15/* Variables for selection control. */
 16/* must not be disallocated */
 17struct vc_data *spk_sel_cons;
 18/* cleared by clear_selection */
 19static int sel_start = -1;
 20static int sel_end;
 21static int sel_buffer_lth;
 22static char *sel_buffer;
 23
 24static unsigned char sel_pos(int n)
 25{
 26	return inverse_translate(spk_sel_cons,
 27		screen_glyph(spk_sel_cons, n), 0);
 28}
 29
 30void speakup_clear_selection(void)
 31{
 32	sel_start = -1;
 33}
 34
 35/* does screen address p correspond to character at LH/RH edge of screen? */
 36static int atedge(const int p, int size_row)
 37{
 38	return !(p % size_row) || !((p + 2) % size_row);
 39}
 40
 41/* constrain v such that v <= u */
 42static unsigned short limit(const unsigned short v, const unsigned short u)
 43{
 44	return (v > u) ? u : v;
 45}
 46
 47int speakup_set_selection(struct tty_struct *tty)
 48{
 49	int new_sel_start, new_sel_end;
 50	char *bp, *obp;
 51	int i, ps, pe;
 52	struct vc_data *vc = vc_cons[fg_console].d;
 53
 54	xs = limit(xs, vc->vc_cols - 1);
 55	ys = limit(ys, vc->vc_rows - 1);
 56	xe = limit(xe, vc->vc_cols - 1);
 57	ye = limit(ye, vc->vc_rows - 1);
 58	ps = ys * vc->vc_size_row + (xs << 1);
 59	pe = ye * vc->vc_size_row + (xe << 1);
 60
 61	if (ps > pe) {
 62		/* make sel_start <= sel_end */
 63		int tmp = ps;
 64		ps = pe;
 65		pe = tmp;
 66	}
 67
 68	if (spk_sel_cons != vc_cons[fg_console].d) {
 69		speakup_clear_selection();
 70		spk_sel_cons = vc_cons[fg_console].d;
 71		printk(KERN_WARNING
 72			"Selection: mark console not the same as cut\n");
 73		return -EINVAL;
 74	}
 75
 76	new_sel_start = ps;
 77	new_sel_end = pe;
 78
 79	/* select to end of line if on trailing space */
 80	if (new_sel_end > new_sel_start &&
 81	    !atedge(new_sel_end, vc->vc_size_row) &&
 82	    ishardspace(sel_pos(new_sel_end))) {
 83		for (pe = new_sel_end + 2; ; pe += 2)
 84			if (!ishardspace(sel_pos(pe)) ||
 85			    atedge(pe, vc->vc_size_row))
 86				break;
 87		if (ishardspace(sel_pos(pe)))
 88			new_sel_end = pe;
 89	}
 90	if ((new_sel_start == sel_start) && (new_sel_end == sel_end))
 91		return 0; /* no action required */
 92
 93	sel_start = new_sel_start;
 94	sel_end = new_sel_end;
 95	/* Allocate a new buffer before freeing the old one ... */
 96	bp = kmalloc((sel_end-sel_start)/2+1, GFP_ATOMIC);
 97	if (!bp) {
 98		printk(KERN_WARNING "selection: kmalloc() failed\n");
 99		speakup_clear_selection();
100		return -ENOMEM;
101	}
102	kfree(sel_buffer);
103	sel_buffer = bp;
104
105	obp = bp;
106	for (i = sel_start; i <= sel_end; i += 2) {
107		*bp = sel_pos(i);
108		if (!ishardspace(*bp++))
109			obp = bp;
110		if (!((i + 2) % vc->vc_size_row)) {
111			/* strip trailing blanks from line and add newline,
112			   unless non-space at end of line. */
113			if (obp != bp) {
114				bp = obp;
115				*bp++ = '\r';
116			}
117			obp = bp;
118		}
119	}
120	sel_buffer_lth = bp - sel_buffer;
121	return 0;
122}
123
124/* TODO: move to some helper thread, probably.  That'd fix having to check for
125 * in_atomic().  */
126int speakup_paste_selection(struct tty_struct *tty)
127{
128	struct vc_data *vc = (struct vc_data *) tty->driver_data;
129	int pasted = 0, count;
130	DECLARE_WAITQUEUE(wait, current);
131	add_wait_queue(&vc->paste_wait, &wait);
132	while (sel_buffer && sel_buffer_lth > pasted) {
133		set_current_state(TASK_INTERRUPTIBLE);
134		if (test_bit(TTY_THROTTLED, &tty->flags)) {
135			if (in_atomic())
136				/* if we are in an interrupt handler, abort */
137				break;
138			schedule();
139			continue;
140		}
141		count = sel_buffer_lth - pasted;
142		count = min_t(int, count, tty->receive_room);
143		tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted,
144			0, count);
145		pasted += count;
146	}
147	remove_wait_queue(&vc->paste_wait, &wait);
148	current->state = TASK_RUNNING;
149	return 0;
150}
151