]> git.ipfire.org Git - thirdparty/util-linux.git/blob - historic/selection/selection.c
Imported from util-linux-2.5 tarball.
[thirdparty/util-linux.git] / historic / selection / selection.c
1 /* implement copying and pasting in Linux virtual consoles */
2 /* Andrew Haylett, 17th June 1993 */
3 /* Wed Feb 15 09:33:16 1995, faith@cs.unc.edu changed tty0 to console, since
4 most systems don't have a tty0 any more. */
5
6 #include <unistd.h>
7 #include <stdio.h>
8 #include <termios.h>
9 #include <fcntl.h>
10 #include <signal.h>
11 #include <errno.h>
12 #include <sys/kd.h>
13 #include <sys/ioctl.h>
14 #include <sys/time.h>
15
16 #include "mouse.h"
17
18 extern int ms_copy_button, ms_paste_button;
19
20 static const int SCALE = 10;
21 static const long CLICK_INTERVAL = 250; /* msec */
22 static const char *console = "/dev/console";
23
24 typedef enum { character = 0, word = 1, line = 2 } sel_mode;
25
26 static int open_console(const int mode);
27 static void set_sel(const int xs, const int ys, const int xe,
28 const int ye, const sel_mode mode);
29 static void paste(void);
30 static long interval(const struct timeval *t1, const struct timeval *t2);
31 static int check_mode(void);
32
33 int
34 main(int argc, char *argv[])
35 {
36 struct ms_event ev;
37 struct winsize win;
38 struct timeval tv1, tv2;
39 int xs, ys, xe, ye, x1, y1, fd, clicks = 0;
40 sel_mode mode;
41
42 fd = open_console(O_RDONLY);
43 ioctl(fd, TIOCGWINSZ, &win);
44 close(fd);
45 if (! win.ws_col || ! win.ws_row)
46 {
47 fprintf(stderr, "selection: zero screen dimension, assuming 80x25.\n");
48 win.ws_col = 80;
49 win.ws_row = 25;
50 }
51
52 ms_params(argc, argv);
53
54 if (ms_init(win.ws_col * SCALE - 1, win.ws_row * SCALE - 1))
55 exit(1);
56
57 if (fork() > 0)
58 exit(0);
59 setsid();
60
61 gettimeofday(&tv1, (struct timezone *)NULL);
62
63 restart:
64 while (1)
65 {
66 if (check_mode())
67 goto restart;
68 if (get_ms_event(&ev))
69 exit(1);
70 if (ev.ev_butstate == ms_copy_button)
71 {
72 ++clicks;
73 gettimeofday(&tv2, (struct timezone *)NULL);
74 xs = ev.ev_x / SCALE + 1;
75 ys = ev.ev_y / SCALE + 1;
76 if (interval(&tv1, &tv2) < CLICK_INTERVAL && clicks == 1)
77 {
78 mode = word;
79 set_sel(xs, ys, xs, ys, mode);
80 }
81 else if (interval(&tv1, &tv2) < CLICK_INTERVAL && clicks == 2)
82 {
83 mode = line;
84 set_sel(xs, ys, xs, ys, mode);
85 }
86 else
87 {
88 mode = character;
89 clicks = 0;
90 do /* wait for left button up */
91 {
92 if (check_mode())
93 goto restart;
94 if (get_ms_event(&ev))
95 exit(1);
96 } while (ev.ev_butstate);
97 x1 = y1 = 0;
98 do /* track start selection until left button down */
99 {
100 xs = ev.ev_x / SCALE + 1;
101 ys = ev.ev_y / SCALE + 1;
102 if (xs != x1 || ys != y1)
103 {
104 set_sel(xs, ys, xs, ys, mode);
105 x1 = xs; y1 = ys;
106 }
107 if (check_mode())
108 goto restart;
109 if (get_ms_event(&ev))
110 exit(1);
111 } while (ev.ev_butstate != ms_copy_button);
112 }
113 x1 = y1 = 0;
114 gettimeofday(&tv1, (struct timezone *)NULL);
115 do /* track end selection until left button up */
116 {
117 xe = ev.ev_x / SCALE + 1;
118 ye = ev.ev_y / SCALE + 1;
119 if (xe != x1 || ye != y1)
120 {
121 set_sel(xs, ys, xe, ye, mode);
122 x1 = xe; y1 = ye;
123 }
124 if (check_mode())
125 goto restart;
126 if (get_ms_event(&ev))
127 exit(1);
128 } while (ev.ev_butstate == ms_copy_button);
129 } else if (ev.ev_butstate == ms_paste_button)
130 { /* paste selection */
131 paste();
132 do /* wait for right button up */
133 {
134 if (check_mode())
135 goto restart;
136 if (get_ms_event(&ev))
137 exit(1);
138 } while (ev.ev_butstate);
139 gettimeofday(&tv1, (struct timezone *)NULL);
140 clicks = 0;
141 }
142 }
143 }
144
145 /* We have to keep opening and closing the console because (a) /dev/tty0
146 changed its behaviour at some point such that the current VC is fixed
147 after the open(), rather than being re-evaluated at each write(), and (b)
148 because we seem to lose our grip on /dev/tty? after someone logs in if
149 this is run from /etc/rc. */
150
151 static int
152 open_console(const int mode)
153 {
154 int fd;
155
156 if ((fd = open(console, mode)) < 0)
157 {
158 perror("selection: open_console()");
159 exit(1);
160 }
161 return fd;
162 }
163
164 /* mark selected text on screen. */
165 static void
166 set_sel(const int xs, const int ys,
167 const int xe, const int ye, const sel_mode mode)
168 {
169 unsigned char buf[sizeof(char) + 5 * sizeof(short)];
170 unsigned short *arg = (unsigned short *)(buf + 1);
171 int fd;
172
173 buf[0] = 2;
174
175 arg[0] = xs;
176 arg[1] = ys;
177 arg[2] = xe;
178 arg[3] = ye;
179 arg[4] = mode;
180
181 fd = open_console(O_WRONLY);
182 if (ioctl(fd, TIOCLINUX, buf) < 0)
183 {
184 perror("selection: ioctl(..., TIOCLINUX, ...)");
185 exit(1);
186 }
187 close(fd);
188 }
189
190 /* paste contents of selection buffer into console. */
191 static void
192 paste(void)
193 {
194 char c = 3;
195 int fd;
196
197 fd = open_console(O_WRONLY);
198 if (ioctl(fd, TIOCLINUX, &c) < 0)
199 {
200 perror("selection: ioctl(..., TIOCLINUX, ...)");
201 exit(1);
202 }
203 close(fd);
204 }
205
206 /* evaluate interval between times. */
207 static long
208 interval(const struct timeval *t1, const struct timeval *t2)
209 {
210 return (t2->tv_sec - t1->tv_sec) * 1000
211 + (t2->tv_usec - t1->tv_usec) / 1000;
212 }
213
214 /* Check whether console is in graphics mode; if so, wait until it isn't. */
215 static int
216 check_mode(void)
217 {
218 int fd, ch = 0;
219 long kd_mode;
220
221 do
222 {
223 fd = open_console(O_RDONLY);
224 if (ioctl(fd, KDGETMODE, &kd_mode) < 0)
225 {
226 perror("selection: ioctl(..., KDGETMODE, ...)");
227 exit(1);
228 }
229 close(fd);
230 if (kd_mode != KD_TEXT)
231 {
232 ++ch;
233 sleep(2);
234 }
235 } while (kd_mode != KD_TEXT);
236 return (ch > 0);
237 }