/*
|
/*
|
* linux/arch/arm/drivers/char/console/selection.c
|
* linux/arch/arm/drivers/char/console/selection.c
|
*
|
*
|
* This module exports the functions:
|
* This module exports the functions:
|
*
|
*
|
* 'int set_selection(const unsigned long arg)'
|
* 'int set_selection(const unsigned long arg)'
|
* 'void clear_selection(void)'
|
* 'void clear_selection(void)'
|
* 'int paste_selection(struct tty_struct *tty)'
|
* 'int paste_selection(struct tty_struct *tty)'
|
* 'int sel_loadlut(const unsigned long arg)'
|
* 'int sel_loadlut(const unsigned long arg)'
|
*
|
*
|
* Now that /dev/vcs exists, most of this can disappear again.
|
* Now that /dev/vcs exists, most of this can disappear again.
|
*/
|
*/
|
|
|
#include <linux/tty.h>
|
#include <linux/tty.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/mm.h>
|
#include <linux/mm.h>
|
#include <linux/malloc.h>
|
#include <linux/malloc.h>
|
#include <linux/types.h>
|
#include <linux/types.h>
|
|
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
|
|
#include "vt_kern.h"
|
#include "vt_kern.h"
|
#include "consolemap.h"
|
#include "consolemap.h"
|
#include "selection.h"
|
#include "selection.h"
|
|
|
#ifndef MIN
|
#ifndef MIN
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
#endif
|
#endif
|
|
|
/* Don't take this from <ctype.h>: 011-015 in the buffer aren't spaces */
|
/* Don't take this from <ctype.h>: 011-015 in the buffer aren't spaces */
|
#define isspace(c) ((c) == ' ')
|
#define isspace(c) ((c) == ' ')
|
|
|
#define sel_pos(n) inverse_translate(scrw2glyph(screen_word(vtdata.select.vt, n)))
|
#define sel_pos(n) inverse_translate(scrw2glyph(screen_word(vtdata.select.vt, n)))
|
|
|
/*
|
/*
|
* clear_selection, highlight and highlight_pointer can be called
|
* clear_selection, highlight and highlight_pointer can be called
|
* from interrupt (via scrollback/front)
|
* from interrupt (via scrollback/front)
|
*/
|
*/
|
|
|
/*
|
/*
|
* set reverse video on characters s-e of console with selection.
|
* set reverse video on characters s-e of console with selection.
|
*/
|
*/
|
static inline void highlight (const int s, const int e)
|
static inline void highlight (const int s, const int e)
|
{
|
{
|
invert_screen(vtdata.select.vt, s, e - s);
|
invert_screen(vtdata.select.vt, s, e - s);
|
}
|
}
|
|
|
/*
|
/*
|
* use complementary color to show the pointer
|
* use complementary color to show the pointer
|
*/
|
*/
|
static inline void highlight_pointer (int where)
|
static inline void highlight_pointer (int where)
|
{
|
{
|
complement_pos(vtdata.select.vt, where);
|
complement_pos(vtdata.select.vt, where);
|
}
|
}
|
|
|
/*
|
/*
|
* Remove the current selection highlight, if any,
|
* Remove the current selection highlight, if any,
|
* from the console holding selection.
|
* from the console holding selection.
|
*/
|
*/
|
void clear_selection (void)
|
void clear_selection (void)
|
{
|
{
|
highlight_pointer(-1); /* hide the pointer */
|
highlight_pointer(-1); /* hide the pointer */
|
if (vtdata.select.start != -1) {
|
if (vtdata.select.start != -1) {
|
highlight(vtdata.select.start, vtdata.select.end);
|
highlight(vtdata.select.start, vtdata.select.end);
|
vtdata.select.start = -1;
|
vtdata.select.start = -1;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* User settable table: what characters are to be considered alphabetic?
|
* User settable table: what characters are to be considered alphabetic?
|
* 256 bits
|
* 256 bits
|
*/
|
*/
|
static u32 inwordLut[8]={
|
static u32 inwordLut[8]={
|
0x00000000, /* control chars */
|
0x00000000, /* control chars */
|
0x03FF0000, /* digits */
|
0x03FF0000, /* digits */
|
0x87FFFFFE, /* uppercase and '_' */
|
0x87FFFFFE, /* uppercase and '_' */
|
0x07FFFFFE, /* lowercase */
|
0x07FFFFFE, /* lowercase */
|
0x00000000,
|
0x00000000,
|
0x00000000,
|
0x00000000,
|
0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */
|
0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */
|
0xFF7FFFFF /* latin-1 accented letters, not division sign */
|
0xFF7FFFFF /* latin-1 accented letters, not division sign */
|
};
|
};
|
|
|
static inline int inword(const unsigned char c)
|
static inline int inword(const unsigned char c)
|
{
|
{
|
return (inwordLut[c>>5] >> (c & 31)) & 1;
|
return (inwordLut[c>>5] >> (c & 31)) & 1;
|
}
|
}
|
|
|
/*
|
/*
|
* set inwordLut contents. Invoked by ioctl().
|
* set inwordLut contents. Invoked by ioctl().
|
*/
|
*/
|
int sel_loadlut(const unsigned long arg)
|
int sel_loadlut(const unsigned long arg)
|
{
|
{
|
int i = verify_area(VERIFY_READ, (char *) arg, 36);
|
int i = verify_area(VERIFY_READ, (char *) arg, 36);
|
if (i)
|
if (i)
|
return i;
|
return i;
|
memcpy_fromfs(inwordLut, (u32 *)(arg+4), 32);
|
memcpy_fromfs(inwordLut, (u32 *)(arg+4), 32);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* does buffer offset p correspond to character at LH/RH edge of screen?
|
* does buffer offset p correspond to character at LH/RH edge of screen?
|
*/
|
*/
|
static inline int atedge(const int p)
|
static inline int atedge(const int p)
|
{
|
{
|
return (!(p % vtdata.numcolumns) || !((p + 1) % vtdata.numcolumns));
|
return (!(p % vtdata.numcolumns) || !((p + 1) % vtdata.numcolumns));
|
}
|
}
|
|
|
/*
|
/*
|
* constrain v such that v <= u
|
* constrain v such that v <= u
|
*/
|
*/
|
static inline int limit (const int v, const int u)
|
static inline int limit (const int v, const int u)
|
{
|
{
|
return ((v > u) ? u : v);
|
return ((v > u) ? u : v);
|
}
|
}
|
|
|
/*
|
/*
|
* set the current selection. Invoked by ioctl().
|
* set the current selection. Invoked by ioctl().
|
*/
|
*/
|
int set_selection (const unsigned long arg, struct tty_struct *tty)
|
int set_selection (const unsigned long arg, struct tty_struct *tty)
|
{
|
{
|
struct vt * vt = vtdata.fgconsole;
|
struct vt * vt = vtdata.fgconsole;
|
int sel_mode, new_sel_start, new_sel_end, spc;
|
int sel_mode, new_sel_start, new_sel_end, spc;
|
char *bp, *obp;
|
char *bp, *obp;
|
int i, ps, pe;
|
int i, ps, pe;
|
|
|
vt_do_unblankscreen ();
|
vt_do_unblankscreen ();
|
|
|
{
|
{
|
unsigned short *args, xs, ys, xe, ye;
|
unsigned short *args, xs, ys, xe, ye;
|
|
|
args = (unsigned short *)(arg + 1);
|
args = (unsigned short *)(arg + 1);
|
xs = get_user (args ++) - 1;
|
xs = get_user (args ++) - 1;
|
ys = get_user (args ++) - 1;
|
ys = get_user (args ++) - 1;
|
xe = get_user (args ++) - 1;
|
xe = get_user (args ++) - 1;
|
ye = get_user (args ++) - 1;
|
ye = get_user (args ++) - 1;
|
sel_mode = get_user (args);
|
sel_mode = get_user (args);
|
|
|
xs = limit (xs, vtdata.numcolumns - 1);
|
xs = limit (xs, vtdata.numcolumns - 1);
|
ys = limit (ys, vtdata.numrows - 1);
|
ys = limit (ys, vtdata.numrows - 1);
|
xe = limit (xe, vtdata.numcolumns - 1);
|
xe = limit (xe, vtdata.numcolumns - 1);
|
ye = limit (ye, vtdata.numrows - 1);
|
ye = limit (ye, vtdata.numrows - 1);
|
ps = ys * vtdata.numcolumns + xs;
|
ps = ys * vtdata.numcolumns + xs;
|
pe = ye * vtdata.numcolumns + xe;
|
pe = ye * vtdata.numcolumns + xe;
|
|
|
if (sel_mode == 4) {
|
if (sel_mode == 4) {
|
/* useful for screendump without selection highlights */
|
/* useful for screendump without selection highlights */
|
clear_selection ();
|
clear_selection ();
|
return 0;
|
return 0;
|
}
|
}
|
|
|
if (vt->vcd->report_mouse && sel_mode & 16) {
|
if (vt->vcd->report_mouse && sel_mode & 16) {
|
mouse_report (tty, sel_mode & 15, xs, ys);
|
mouse_report (tty, sel_mode & 15, xs, ys);
|
return 0;
|
return 0;
|
}
|
}
|
}
|
}
|
|
|
if (ps > pe) { /* make sel_start <= sel_end */
|
if (ps > pe) { /* make sel_start <= sel_end */
|
ps ^= pe;
|
ps ^= pe;
|
pe ^= ps;
|
pe ^= ps;
|
ps ^= pe;
|
ps ^= pe;
|
}
|
}
|
|
|
if (vt != vtdata.select.vt) {
|
if (vt != vtdata.select.vt) {
|
clear_selection ();
|
clear_selection ();
|
vtdata.select.vt = vt;
|
vtdata.select.vt = vt;
|
}
|
}
|
|
|
switch (sel_mode) {
|
switch (sel_mode) {
|
case 0: /* character-by-character selection */
|
case 0: /* character-by-character selection */
|
new_sel_start = ps;
|
new_sel_start = ps;
|
new_sel_end = pe;
|
new_sel_end = pe;
|
break;
|
break;
|
case 1: /* word-by-word selection */
|
case 1: /* word-by-word selection */
|
spc = isspace (sel_pos (ps));
|
spc = isspace (sel_pos (ps));
|
for (new_sel_start = ps; ; ps --) {
|
for (new_sel_start = ps; ; ps --) {
|
if (( spc && !isspace (sel_pos (ps))) ||
|
if (( spc && !isspace (sel_pos (ps))) ||
|
(!spc && !inword (sel_pos (ps))))
|
(!spc && !inword (sel_pos (ps))))
|
break;
|
break;
|
new_sel_start = ps;
|
new_sel_start = ps;
|
if (!(ps % vtdata.numcolumns))
|
if (!(ps % vtdata.numcolumns))
|
break;
|
break;
|
}
|
}
|
spc = isspace (sel_pos (pe));
|
spc = isspace (sel_pos (pe));
|
for (new_sel_end = pe; ; pe ++) {
|
for (new_sel_end = pe; ; pe ++) {
|
if (( spc && !isspace (sel_pos (pe))) ||
|
if (( spc && !isspace (sel_pos (pe))) ||
|
(!spc && !inword (sel_pos (pe))))
|
(!spc && !inword (sel_pos (pe))))
|
break;
|
break;
|
new_sel_end = pe;
|
new_sel_end = pe;
|
if (!((pe + 1) % vtdata.numcolumns))
|
if (!((pe + 1) % vtdata.numcolumns))
|
break;
|
break;
|
}
|
}
|
break;
|
break;
|
case 2: /* line-by-line selection */
|
case 2: /* line-by-line selection */
|
new_sel_start = ps - ps % vtdata.numcolumns;
|
new_sel_start = ps - ps % vtdata.numcolumns;
|
new_sel_end = pe + vtdata.numcolumns - pe % vtdata.numcolumns - 1;
|
new_sel_end = pe + vtdata.numcolumns - pe % vtdata.numcolumns - 1;
|
break;
|
break;
|
case 3:
|
case 3:
|
highlight_pointer (pe);
|
highlight_pointer (pe);
|
return 0;
|
return 0;
|
default:
|
default:
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
|
|
/* remove the pointer */
|
/* remove the pointer */
|
highlight_pointer (-1);
|
highlight_pointer (-1);
|
|
|
/* select to end of line if on trailing space */
|
/* select to end of line if on trailing space */
|
if (new_sel_end > new_sel_start && !atedge(new_sel_end) && isspace(sel_pos(new_sel_end))) {
|
if (new_sel_end > new_sel_start && !atedge(new_sel_end) && isspace(sel_pos(new_sel_end))) {
|
for (pe = new_sel_end + 1; ; pe ++)
|
for (pe = new_sel_end + 1; ; pe ++)
|
if (!isspace (sel_pos (pe)) || atedge (pe))
|
if (!isspace (sel_pos (pe)) || atedge (pe))
|
break;
|
break;
|
if (isspace (sel_pos (pe)))
|
if (isspace (sel_pos (pe)))
|
new_sel_end = pe;
|
new_sel_end = pe;
|
}
|
}
|
if (vtdata.select.start == -1) /* no current selection */
|
if (vtdata.select.start == -1) /* no current selection */
|
highlight (new_sel_start, new_sel_end);
|
highlight (new_sel_start, new_sel_end);
|
else if (new_sel_start == vtdata.select.start) {
|
else if (new_sel_start == vtdata.select.start) {
|
if (new_sel_end == vtdata.select.end) /* no action required */
|
if (new_sel_end == vtdata.select.end) /* no action required */
|
return 0;
|
return 0;
|
else if (new_sel_end > vtdata.select.end) /* extend to right */
|
else if (new_sel_end > vtdata.select.end) /* extend to right */
|
highlight (vtdata.select.end + 1, new_sel_end);
|
highlight (vtdata.select.end + 1, new_sel_end);
|
else /* contract from right */
|
else /* contract from right */
|
highlight (new_sel_end + 1, vtdata.select.end);
|
highlight (new_sel_end + 1, vtdata.select.end);
|
} else if (new_sel_end == vtdata.select.end) {
|
} else if (new_sel_end == vtdata.select.end) {
|
if (new_sel_start < vtdata.select.start) /* extend to left */
|
if (new_sel_start < vtdata.select.start) /* extend to left */
|
highlight (new_sel_start, vtdata.select.start - 1);
|
highlight (new_sel_start, vtdata.select.start - 1);
|
else /* contract from left */
|
else /* contract from left */
|
highlight (vtdata.select.start, new_sel_start - 1);
|
highlight (vtdata.select.start, new_sel_start - 1);
|
} else { /* some other case; start selection from scratch */
|
} else { /* some other case; start selection from scratch */
|
clear_selection ();
|
clear_selection ();
|
highlight (new_sel_start, new_sel_end);
|
highlight (new_sel_start, new_sel_end);
|
}
|
}
|
vtdata.select.start = new_sel_start;
|
vtdata.select.start = new_sel_start;
|
vtdata.select.end = new_sel_end;
|
vtdata.select.end = new_sel_end;
|
|
|
if (vtdata.select.buffer)
|
if (vtdata.select.buffer)
|
kfree (vtdata.select.buffer);
|
kfree (vtdata.select.buffer);
|
vtdata.select.buffer = kmalloc (vtdata.select.end - vtdata.select.start + 1, GFP_KERNEL);
|
vtdata.select.buffer = kmalloc (vtdata.select.end - vtdata.select.start + 1, GFP_KERNEL);
|
if (!vtdata.select.buffer) {
|
if (!vtdata.select.buffer) {
|
printk ("selection: kmalloc() failed\n");
|
printk ("selection: kmalloc() failed\n");
|
clear_selection ();
|
clear_selection ();
|
return -ENOMEM;
|
return -ENOMEM;
|
}
|
}
|
|
|
obp = bp = vtdata.select.buffer;
|
obp = bp = vtdata.select.buffer;
|
for (i = vtdata.select.start; i <= vtdata.select.end; i++) {
|
for (i = vtdata.select.start; i <= vtdata.select.end; i++) {
|
*bp = sel_pos (i);
|
*bp = sel_pos (i);
|
if (!isspace (*bp++))
|
if (!isspace (*bp++))
|
obp = bp;
|
obp = bp;
|
if (!((i + 1) % vtdata.numcolumns)) {
|
if (!((i + 1) % vtdata.numcolumns)) {
|
/* strip trailing blanks from line and add newline,
|
/* strip trailing blanks from line and add newline,
|
* unless non-space at end of line.
|
* unless non-space at end of line.
|
*/
|
*/
|
if (obp != bp) {
|
if (obp != bp) {
|
bp = obp;
|
bp = obp;
|
*bp++ = '\r';
|
*bp++ = '\r';
|
}
|
}
|
obp = bp;
|
obp = bp;
|
}
|
}
|
}
|
}
|
vtdata.select.length = bp - vtdata.select.buffer;
|
vtdata.select.length = bp - vtdata.select.buffer;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Insert the contents of the selection buffer into the queue of the
|
/* Insert the contents of the selection buffer into the queue of the
|
* tty associated with the current console. Invoked by ioctl().
|
* tty associated with the current console. Invoked by ioctl().
|
*/
|
*/
|
int paste_selection (struct tty_struct *tty)
|
int paste_selection (struct tty_struct *tty)
|
{
|
{
|
struct wait_queue wait = { current, NULL };
|
struct wait_queue wait = { current, NULL };
|
struct vt_struct *vt = ((struct vt *)tty->driver_data)->vtd;
|
struct vt_struct *vt = ((struct vt *)tty->driver_data)->vtd;
|
char *bp = vtdata.select.buffer;
|
char *bp = vtdata.select.buffer;
|
int c = vtdata.select.length;
|
int c = vtdata.select.length;
|
int l;
|
int l;
|
|
|
if (!bp || !c)
|
if (!bp || !c)
|
return 0;
|
return 0;
|
vt_do_unblankscreen ();
|
vt_do_unblankscreen ();
|
add_wait_queue(&vt->paste_wait, &wait);
|
add_wait_queue(&vt->paste_wait, &wait);
|
do {
|
do {
|
current->state = TASK_INTERRUPTIBLE;
|
current->state = TASK_INTERRUPTIBLE;
|
if (test_bit(TTY_THROTTLED, &tty->flags)) {
|
if (test_bit(TTY_THROTTLED, &tty->flags)) {
|
schedule();
|
schedule();
|
continue;
|
continue;
|
}
|
}
|
l = MIN(c, tty->ldisc.receive_room(tty));
|
l = MIN(c, tty->ldisc.receive_room(tty));
|
tty->ldisc.receive_buf(tty, bp, 0, l);
|
tty->ldisc.receive_buf(tty, bp, 0, l);
|
c -= l;
|
c -= l;
|
bp += l;
|
bp += l;
|
} while (c);
|
} while (c);
|
remove_wait_queue(&vt->paste_wait, &wait);
|
remove_wait_queue(&vt->paste_wait, &wait);
|
current->state = TASK_RUNNING;
|
current->state = TASK_RUNNING;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|