]> git.proxmox.com Git - spiceterm.git/blame - spiceterm.c
move spice input/vdagent related code to input.c
[spiceterm.git] / spiceterm.c
CommitLineData
22e5ba02
DM
1/*
2
4ef70811 3 Copyright (C) 2013 Proxmox Server Solutions GmbH
22e5ba02 4
4ef70811 5 Copyright: spiceterm is under GNU GPL, the GNU General Public License.
22e5ba02 6
4ef70811
DM
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; version 2 dated June, 1991.
22e5ba02 10
4ef70811
DM
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
22e5ba02 15
4ef70811
DM
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
22e5ba02 20
4ef70811
DM
21 Author: Dietmar Maurer <dietmar@proxmox.com>
22
0e80a2f3 23 Note: most of the code here is copied from vncterm (which is
4ef70811 24 also written by me).
22e5ba02
DM
25
26*/
27
0e80a2f3 28#define _GNU_SOURCE
abc13312 29
22e5ba02
DM
30#include <stdio.h>
31#include <stdlib.h>
32#include <unistd.h>
3affb0e7 33#include <sys/types.h>
22e5ba02
DM
34#include <sys/socket.h>
35#include <arpa/inet.h>
36#include <netdb.h>
37#include <pty.h> /* for openpty and forkpty */
38#include <string.h>
39#include <errno.h>
40#include <sys/ioctl.h>
41#include <sys/wait.h>
42#include <signal.h>
43#include <locale.h>
44
45#include "spiceterm.h"
22e5ba02 46
63a34f43 47#include <glib.h>
22e5ba02
DM
48#include <spice.h>
49#include <spice/enums.h>
50#include <spice/macros.h>
51#include <spice/qxl_dev.h>
52
c46a8251 53#include <gdk/gdkkeysyms.h>
22e5ba02 54
64bc7a2f 55#include "event_loop.h"
e3759a34 56#include "translations.h"
8c22ae7f 57
a0579497 58static int debug = 0;
0e80a2f3 59
a0579497
DM
60#define DPRINTF(x, format, ...) { \
61 if (x <= debug) { \
62 printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \
63 } \
64}
22e5ba02
DM
65
66#define TERM "xterm"
67
68#define TERMIDCODE "[?1;2c" // vt100 ID
69
70#define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
a0579497
DM
71 fprintf(stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
72 print_usage(NULL); \
22e5ba02
DM
73 exit(1); \
74}
75
76/* these colours are from linux kernel drivers/char/vt.c */
77
22e5ba02
DM
78unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
79 8,12,10,14, 9,13,11,15 };
80
22e5ba02 81static void
0e80a2f3 82print_usage(const char *msg)
22e5ba02 83{
0e80a2f3
DM
84 if (msg) { fprintf(stderr, "ERROR: %s\n", msg); }
85 fprintf(stderr, "USAGE: spiceterm [spiceopts] [-c command [args]]\n");
22e5ba02
DM
86}
87
22e5ba02 88static void
ea7edec4 89draw_char_at(spiceTerm *vt, int x, int y, gunichar2 ch, TextAttributes attrib)
22e5ba02 90{
3affb0e7
DM
91 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) {
92 return;
22e5ba02
DM
93 }
94
64bc7a2f 95 spice_screen_draw_char(vt->screen, x, y, ch, attrib);
22e5ba02
DM
96}
97
98static void
ea7edec4 99spiceterm_update_xy(spiceTerm *vt, int x, int y)
22e5ba02 100{
0e80a2f3 101 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
22e5ba02 102
0e80a2f3
DM
103 int y1 = (vt->y_base + y) % vt->total_height;
104 int y2 = y1 - vt->y_displ;
105 if (y2 < 0) {
106 y2 += vt->total_height;
107 }
108 if (y2 < vt->height) {
109 TextCell *c = &vt->cells[y1 * vt->width + x];
ea7edec4 110 draw_char_at(vt, x, y2, c->ch, c->attrib);
0e80a2f3 111 }
22e5ba02
DM
112}
113
114static void
ea7edec4 115spiceterm_clear_xy(spiceTerm *vt, int x, int y)
22e5ba02 116{
0e80a2f3 117 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
22e5ba02 118
0e80a2f3
DM
119 int y1 = (vt->y_base + y) % vt->total_height;
120 int y2 = y1 - vt->y_displ;
121 if (y2 < 0) {
122 y2 += vt->total_height;
123 }
124 if (y2 < vt->height) {
125 TextCell *c = &vt->cells[y1 * vt->width + x];
126 c->ch = ' ';
127 c->attrib = vt->default_attrib;
128 c->attrib.fgcol = vt->cur_attrib.fgcol;
129 c->attrib.bgcol = vt->cur_attrib.bgcol;
130
ea7edec4 131 draw_char_at(vt, x, y, c->ch, c->attrib);
0e80a2f3 132 }
22e5ba02
DM
133}
134
1d11aa12
DM
135void
136spiceterm_toggle_marked_cell(spiceTerm *vt, int pos)
137{
138 int x = (pos%vt->width);
139 int y = (pos/vt->width);
140
141 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
142
7d75ff3d
DM
143 int y1 = (vt->y_displ + y) % vt->total_height;
144
145 TextCell *c = &vt->cells[y1 * vt->width + x];
146 c->attrib.selected = c->attrib.selected ? 0 : 1;
147
148 if (y < vt->height) {
149 draw_char_at(vt, x, y, c->ch, c->attrib);
1d11aa12
DM
150 }
151}
152
22e5ba02 153static void
ea7edec4 154spiceterm_show_cursor(spiceTerm *vt, int show)
22e5ba02 155{
0e80a2f3
DM
156 int x = vt->cx;
157 if (x >= vt->width) {
158 x = vt->width - 1;
159 }
22e5ba02 160
0e80a2f3
DM
161 int y1 = (vt->y_base + vt->cy) % vt->total_height;
162 int y = y1 - vt->y_displ;
163 if (y < 0) {
164 y += vt->total_height;
165 }
22e5ba02 166
0e80a2f3 167 if (y < vt->height) {
22e5ba02 168
0e80a2f3 169 TextCell *c = &vt->cells[y1 * vt->width + x];
22e5ba02 170
0e80a2f3
DM
171 if (show) {
172 TextAttributes attrib = vt->default_attrib;
173 attrib.invers = !(attrib.invers); /* invert fg and bg */
ea7edec4 174 draw_char_at(vt, x, y, c->ch, attrib);
0e80a2f3 175 } else {
ea7edec4 176 draw_char_at(vt, x, y, c->ch, c->attrib);
0e80a2f3 177 }
22e5ba02 178 }
22e5ba02
DM
179}
180
f424be98 181void
7d75ff3d 182spiceterm_refresh(spiceTerm *vt)
22e5ba02 183{
0e80a2f3 184 int x, y, y1;
22e5ba02 185
0e80a2f3
DM
186 y1 = vt->y_displ;
187 for(y = 0; y < vt->height; y++) {
188 TextCell *c = vt->cells + y1 * vt->width;
189 for(x = 0; x < vt->width; x++) {
7d75ff3d 190 draw_char_at(vt, x, y, c->ch, c->attrib);
0e80a2f3
DM
191 c++;
192 }
193 if (++y1 == vt->total_height)
194 y1 = 0;
22e5ba02 195 }
22e5ba02 196
7d75ff3d
DM
197 spiceterm_show_cursor(vt, 1);
198}
199
e420a6d9
DM
200static void
201spiceterm_clear_screen(spiceTerm *vt)
202{
203 int x, y;
204
205 for (y = 0; y <= vt->height; y++) {
206 int y1 = (vt->y_base + y) % vt->total_height;
207 TextCell *c = &vt->cells[y1 * vt->width];
208 for (x = 0; x < vt->width; x++) {
209 c->ch = ' ';
210 c->attrib = vt->default_attrib;
211 c->attrib.fgcol = vt->cur_attrib.fgcol;
212 c->attrib.bgcol = vt->cur_attrib.bgcol;
213
214 c++;
215 }
216 }
217
218 spice_screen_clear(vt->screen, 0, 0, vt->screen->primary_width,
219 vt->screen->primary_height);
220}
221
7d75ff3d
DM
222void
223spiceterm_unselect_all(spiceTerm *vt)
224{
37c2d30f 225 int x, y, y1;
7d75ff3d 226
37c2d30f
DM
227 y1 = vt->y_displ;
228 for(y = 0; y < vt->total_height; y++) {
229 TextCell *c = vt->cells + y1 * vt->width;
230 for(x = 0; x < vt->width; x++) {
231 if (c->attrib.selected) {
232 c->attrib.selected = 0;
233 if (y < vt->height) {
234 draw_char_at(vt, x, y, c->ch, c->attrib);
235 }
236 }
237 c++;
7d75ff3d 238 }
37c2d30f
DM
239 if (++y1 == vt->total_height)
240 y1 = 0;
7d75ff3d 241 }
22e5ba02
DM
242}
243
244static void
ea7edec4 245spiceterm_scroll_down(spiceTerm *vt, int top, int bottom, int lines)
22e5ba02 246{
7b4a7650
DM
247 if ((top + lines) >= bottom) {
248 lines = bottom - top -1;
249 }
22e5ba02 250
7b4a7650
DM
251 if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
252 return;
253 }
22e5ba02 254
7b4a7650
DM
255 int i;
256 for(i = bottom - top - lines - 1; i >= 0; i--) {
257 int src = ((vt->y_base + top + i) % vt->total_height)*vt->width;
258 int dst = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
22e5ba02 259
ea7edec4 260 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof(TextCell));
7b4a7650 261 }
22e5ba02 262
7b4a7650
DM
263 for (i = 0; i < lines; i++) {
264 int j;
265 TextCell *c = vt->cells + ((vt->y_base + top + i) % vt->total_height)*vt->width;
266 for(j = 0; j < vt->width; j++) {
267 c->attrib = vt->default_attrib;
268 c->ch = ' ';
269 c++;
270 }
271 }
22e5ba02 272
7b4a7650
DM
273 int h = lines * 16;
274 int y0 = top*16;
275 int y1 = y0 + h;
276 int y2 = bottom*16;
22e5ba02 277
64bc7a2f
DM
278 spice_screen_scroll(vt->screen, 0, y1, vt->screen->primary_width, y2, 0, y0);
279 spice_screen_clear(vt->screen, 0, y0, vt->screen->primary_width, y1);
22e5ba02
DM
280}
281
282static void
ea7edec4 283spiceterm_scroll_up(spiceTerm *vt, int top, int bottom, int lines, int moveattr)
22e5ba02 284{
7b4a7650
DM
285 if ((top + lines) >= bottom) {
286 lines = bottom - top - 1;
287 }
22e5ba02 288
7b4a7650
DM
289 if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
290 return;
291 }
22e5ba02 292
22e5ba02 293
7b4a7650
DM
294 int h = lines * 16;
295 int y0 = top*16;
296 int y1 = (top + lines)*16;
297 int y2 = bottom*16;
22e5ba02 298
64bc7a2f
DM
299 spice_screen_scroll(vt->screen, 0, y0, vt->screen->primary_width, y2 -h, 0, y1);
300 spice_screen_clear(vt->screen, 0, y2 -h, vt->screen->primary_width, y2);
0e80a2f3 301
7b4a7650
DM
302 if (!moveattr) {
303 return;
304 }
22e5ba02 305
7b4a7650 306 // move attributes
22e5ba02 307
7b4a7650
DM
308 int i;
309 for(i = 0; i < (bottom - top - lines); i++) {
310 int dst = ((vt->y_base + top + i) % vt->total_height)*vt->width;
311 int src = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
22e5ba02 312
ea7edec4 313 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof(TextCell));
7b4a7650 314 }
22e5ba02 315
7b4a7650
DM
316 for (i = 1; i <= lines; i++) {
317 int j;
318 TextCell *c = vt->cells + ((vt->y_base + bottom - i) % vt->total_height)*vt->width;
319 for(j = 0; j < vt->width; j++) {
320 c->attrib = vt->default_attrib;
321 c->ch = ' ';
322 c++;
323 }
22e5ba02 324 }
22e5ba02
DM
325}
326
f424be98 327void
ea7edec4 328spiceterm_virtual_scroll(spiceTerm *vt, int lines)
22e5ba02 329{
0e80a2f3
DM
330 if (vt->altbuf || lines == 0) return;
331
332 if (lines < 0) {
333 lines = -lines;
334 int i = vt->scroll_height;
335 if (i > vt->total_height - vt->height)
336 i = vt->total_height - vt->height;
337 int y1 = vt->y_base - i;
338 if (y1 < 0)
339 y1 += vt->total_height;
340 for(i = 0; i < lines; i++) {
341 if (vt->y_displ == y1) break;
342 if (--vt->y_displ < 0) {
343 vt->y_displ = vt->total_height - 1;
344 }
345 }
346 } else {
347 int i;
348 for(i = 0; i < lines; i++) {
349 if (vt->y_displ == vt->y_base) break;
350 if (++vt->y_displ == vt->total_height) {
351 vt->y_displ = 0;
352 }
353 }
22e5ba02
DM
354 }
355
ea7edec4 356 spiceterm_refresh(vt);
22e5ba02 357}
0e80a2f3 358
f424be98 359void
ea7edec4 360spiceterm_respond_esc(spiceTerm *vt, const char *esc)
22e5ba02 361{
ea7edec4 362 int len = strlen(esc);
0e80a2f3 363 int i;
22e5ba02 364
0e80a2f3
DM
365 if (vt->ibuf_count < (IBUFSIZE - 1 - len)) {
366 vt->ibuf[vt->ibuf_count++] = 27;
367 for (i = 0; i < len; i++) {
368 vt->ibuf[vt->ibuf_count++] = esc[i];
369 }
f424be98
DM
370 } else {
371 fprintf(stderr, "input buffer oferflow\n");
372 return;
373 }
374}
375
376void
377spiceterm_respond_data(spiceTerm *vt, int len, uint8_t *data)
378{
379 int i;
380
381 if (vt->ibuf_count < (IBUFSIZE - len)) {
382 for (i = 0; i < len; i++) {
383 vt->ibuf[vt->ibuf_count++] = data[i];
384 }
385 } else {
386 fprintf(stderr, "input buffer oferflow\n");
387 return;
22e5ba02 388 }
22e5ba02
DM
389}
390
391static void
ea7edec4 392spiceterm_put_lf(spiceTerm *vt)
22e5ba02 393{
0e80a2f3 394 if (vt->cy + 1 == vt->region_bottom) {
22e5ba02 395
0e80a2f3 396 if (vt->altbuf || vt->region_top != 0 || vt->region_bottom != vt->height) {
ea7edec4 397 spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 1);
0e80a2f3
DM
398 return;
399 }
22e5ba02 400
0e80a2f3 401 if (vt->y_displ == vt->y_base) {
ea7edec4 402 spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 0);
0e80a2f3 403 }
22e5ba02 404
0e80a2f3
DM
405 if (vt->y_displ == vt->y_base) {
406 if (++vt->y_displ == vt->total_height) {
407 vt->y_displ = 0;
408 }
409 }
22e5ba02 410
0e80a2f3
DM
411 if (++vt->y_base == vt->total_height) {
412 vt->y_base = 0;
413 }
22e5ba02 414
0e80a2f3
DM
415 if (vt->scroll_height < vt->total_height) {
416 vt->scroll_height++;
417 }
22e5ba02 418
0e80a2f3
DM
419 int y1 = (vt->y_base + vt->height - 1) % vt->total_height;
420 TextCell *c = &vt->cells[y1 * vt->width];
421 int x;
422 for (x = 0; x < vt->width; x++) {
423 c->ch = ' ';
424 c->attrib = vt->default_attrib;
425 c++;
426 }
22e5ba02 427
0e80a2f3 428 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
22e5ba02 429
0e80a2f3
DM
430 } else if (vt->cy < vt->height - 1) {
431 vt->cy += 1;
432 }
22e5ba02
DM
433}
434
22e5ba02 435static void
ea7edec4 436spiceterm_csi_m(spiceTerm *vt)
22e5ba02 437{
0e80a2f3 438 int i;
22e5ba02 439
0e80a2f3
DM
440 for (i = 0; i < vt->esc_count; i++) {
441 switch (vt->esc_buf[i]) {
442 case 0: /* reset all console attributes to default */
443 vt->cur_attrib = vt->default_attrib;
444 break;
445 case 1:
446 vt->cur_attrib.bold = 1;
447 break;
448 case 4:
449 vt->cur_attrib.uline = 1;
450 break;
451 case 5:
452 vt->cur_attrib.blink = 1;
453 break;
454 case 7:
455 vt->cur_attrib.invers = 1;
456 break;
457 case 8:
458 vt->cur_attrib.unvisible = 1;
459 break;
460 case 10:
461 vt->cur_enc = LAT1_MAP;
462 // fixme: dispaly controls = 0 ?
463 // fixme: toggle meta = 0 ?
464 break;
465 case 11:
466 vt->cur_enc = IBMPC_MAP;
467 // fixme: dispaly controls = 1 ?
468 // fixme: toggle meta = 0 ?
469 break;
470 case 12:
471 vt->cur_enc = IBMPC_MAP;
472 // fixme: dispaly controls = 1 ?
473 // fixme: toggle meta = 1 ?
474 break;
475 case 22:
476 vt->cur_attrib.bold = 0;
477 break;
478 case 24:
479 vt->cur_attrib.uline = 0;
480 break;
481 case 25:
482 vt->cur_attrib.blink = 0;
483 break;
484 case 27:
485 vt->cur_attrib.invers = 0;
486 break;
487 case 28:
488 vt->cur_attrib.unvisible = 0;
489 break;
490 case 30:
491 case 31:
492 case 32:
493 case 33:
494 case 34:
495 case 35:
496 case 36:
497 case 37:
498 /* set foreground color */
499 vt->cur_attrib.fgcol = color_table [vt->esc_buf[i] - 30];
500 break;
501 case 38:
502 /* reset color to default, enable underline */
503 vt->cur_attrib.fgcol = vt->default_attrib.fgcol;
504 vt->cur_attrib.uline = 1;
505 break;
506 case 39:
507 /* reset color to default, disable underline */
508 vt->cur_attrib.fgcol = vt->default_attrib.fgcol;
509 vt->cur_attrib.uline = 0;
510 break;
511 case 40:
512 case 41:
513 case 42:
514 case 43:
515 case 44:
516 case 45:
517 case 46:
518 case 47:
519 /* set background color */
520 vt->cur_attrib.bgcol = color_table [vt->esc_buf[i] - 40];
521 break;
522 case 49:
523 /* reset background color */
524 vt->cur_attrib.bgcol = vt->default_attrib.bgcol;
525 break;
526 default:
527 fprintf(stderr, "unhandled ESC[%d m code\n",vt->esc_buf[i]);
528 //fixme: implement
529 }
530 }
22e5ba02
DM
531}
532
533static void
ea7edec4 534spiceterm_save_cursor(spiceTerm *vt)
22e5ba02 535{
0e80a2f3
DM
536 vt->cx_saved = vt->cx;
537 vt->cy_saved = vt->cy;
538 vt->cur_attrib_saved = vt->cur_attrib;
539 vt->charset_saved = vt->charset;
540 vt->g0enc_saved = vt->g0enc;
541 vt->g1enc_saved = vt->g1enc;
542 vt->cur_enc_saved = vt->cur_enc;
22e5ba02
DM
543}
544
545static void
ea7edec4 546spiceterm_restore_cursor(spiceTerm *vt)
22e5ba02 547{
0e80a2f3
DM
548 vt->cx = vt->cx_saved;
549 vt->cy = vt->cy_saved;
550 vt->cur_attrib = vt->cur_attrib_saved;
551 vt->charset = vt->charset_saved;
552 vt->g0enc = vt->g0enc_saved;
553 vt->g1enc = vt->g1enc_saved;
554 vt->cur_enc = vt->cur_enc_saved;
22e5ba02
DM
555}
556
557static void
ea7edec4 558spiceterm_set_alternate_buffer(spiceTerm *vt, int on_off)
22e5ba02 559{
0e80a2f3 560 int x, y;
22e5ba02 561
0e80a2f3 562 vt->y_displ = vt->y_base;
22e5ba02 563
0e80a2f3 564 if (on_off) {
22e5ba02 565
0e80a2f3 566 if (vt->altbuf) return;
22e5ba02 567
0e80a2f3 568 vt->altbuf = 1;
22e5ba02 569
0e80a2f3 570 /* alternate buffer & cursor */
22e5ba02 571
ea7edec4 572 spiceterm_save_cursor(vt);
0e80a2f3
DM
573 /* save screen to altcels */
574 for (y = 0; y < vt->height; y++) {
575 int y1 = (vt->y_base + y) % vt->total_height;
576 for (x = 0; x < vt->width; x++) {
577 vt->altcells[y*vt->width + x] = vt->cells[y1*vt->width + x];
578 }
579 }
22e5ba02 580
0e80a2f3
DM
581 /* clear screen */
582 for (y = 0; y <= vt->height; y++) {
583 for (x = 0; x < vt->width; x++) {
186d67d6 584 // spiceterm_clear_xy(vt, x, y);
0e80a2f3
DM
585 }
586 }
424ef03c 587
0e80a2f3 588 } else {
424ef03c 589
0e80a2f3 590 if (vt->altbuf == 0) return;
22e5ba02 591
0e80a2f3 592 vt->altbuf = 0;
22e5ba02 593
0e80a2f3
DM
594 /* restore saved data */
595 for (y = 0; y < vt->height; y++) {
596 int y1 = (vt->y_base + y) % vt->total_height;
597 for (x = 0; x < vt->width; x++) {
598 vt->cells[y1*vt->width + x] = vt->altcells[y*vt->width + x];
599 }
600 }
22e5ba02 601
ea7edec4 602 spiceterm_restore_cursor(vt);
22e5ba02
DM
603 }
604
ea7edec4 605 spiceterm_refresh(vt);
22e5ba02
DM
606}
607
608static void
ea7edec4 609spiceterm_set_mode(spiceTerm *vt, int on_off)
22e5ba02 610{
0e80a2f3 611 int i;
22e5ba02 612
0e80a2f3
DM
613 for (i = 0; i <= vt->esc_count; i++) {
614 if (vt->esc_ques) { /* DEC private modes set/reset */
615 switch(vt->esc_buf[i]) {
616 case 10: /* X11 mouse reporting on/off */
ded68f44
DM
617 case 1000: /* SET_VT200_MOUSE */
618 case 1002: /* xterm SET_BTN_EVENT_MOUSE */
0e80a2f3
DM
619 vt->report_mouse = on_off;
620 break;
621 case 1049: /* start/end special app mode (smcup/rmcup) */
622 spiceterm_set_alternate_buffer (vt, on_off);
623 break;
624 case 25: /* Cursor on/off */
ded68f44 625 case 9: /* X10 mouse reporting on/off */
0e80a2f3
DM
626 case 6: /* Origin relative/absolute */
627 case 1: /* Cursor keys in appl mode*/
628 case 5: /* Inverted screen on/off */
629 case 7: /* Autowrap on/off */
630 case 8: /* Autorepeat on/off */
631 break;
ded68f44 632 }
0e80a2f3 633 } else { /* ANSI modes set/reset */
ded68f44
DM
634 //g_assert_not_reached();
635
0e80a2f3
DM
636 /* fixme: implement me */
637 }
22e5ba02 638 }
22e5ba02
DM
639}
640
641static void
ea7edec4 642spiceterm_gotoxy(spiceTerm *vt, int x, int y)
22e5ba02 643{
0e80a2f3 644 /* verify all boundaries */
22e5ba02 645
0e80a2f3
DM
646 if (x < 0) {
647 x = 0;
648 }
22e5ba02 649
0e80a2f3
DM
650 if (x >= vt->width) {
651 x = vt->width - 1;
652 }
22e5ba02 653
0e80a2f3 654 vt->cx = x;
22e5ba02 655
0e80a2f3
DM
656 if (y < 0) {
657 y = 0;
658 }
22e5ba02 659
0e80a2f3
DM
660 if (y >= vt->height) {
661 y = vt->height - 1;
662 }
22e5ba02 663
0e80a2f3 664 vt->cy = y;
22e5ba02
DM
665}
666
ea7edec4 667static void
a3fd8291
DM
668debug_print_escape_buffer(spiceTerm *vt, const char *func, const char *prefix,
669 const char *qes, gunichar2 ch)
670{
671 if (debug >=1 ) {
672 if (vt->esc_count == 0) {
673 printf("%s:%s ESC[%s%c\n", func, prefix, qes, ch);
674 } else if (vt->esc_count == 1) {
675 printf("%s:%s ESC[%s%d%c\n", func, prefix, qes, vt->esc_buf[0], ch);
676 } else {
677 int i;
678 printf("%s:%s ESC[%s%d", func, prefix, qes, vt->esc_buf[0]);
679 for (i = 1; i < vt->esc_count; i++) {
680 printf(";%d", vt->esc_buf[i]);
681 }
682 printf("%c\n", ch);
683 }
684 }
685}
686
22e5ba02
DM
687enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
688 EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
689 ESpalette, ESidquery, ESosc1, ESosc2};
690
691static void
ea7edec4 692spiceterm_putchar(spiceTerm *vt, gunichar2 ch)
22e5ba02 693{
0e80a2f3 694 int x, y, i, c;
22e5ba02 695
0e80a2f3 696 if (debug && !vt->tty_state) {
a3fd8291 697 DPRINTF(1, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d",
0e80a2f3 698 vt->tty_state, ch, ch, vt->cur_enc, vt->cx, vt->cy);
22e5ba02
DM
699 }
700
0e80a2f3
DM
701 switch(vt->tty_state) {
702 case ESesc:
703 vt->tty_state = ESnormal;
704 switch (ch) {
705 case '[':
706 vt->tty_state = ESsquare;
707 break;
708 case ']':
709 vt->tty_state = ESnonstd;
710 break;
711 case '%':
712 vt->tty_state = ESpercent;
713 break;
714 case '7':
ea7edec4 715 spiceterm_save_cursor(vt);
0e80a2f3
DM
716 break;
717 case '8':
ea7edec4 718 spiceterm_restore_cursor(vt);
0e80a2f3
DM
719 break;
720 case '(':
721 vt->tty_state = ESsetG0; // SET G0
722 break;
723 case ')':
724 vt->tty_state = ESsetG1; // SET G1
725 break;
726 case 'M':
727 /* cursor up (ri) */
728 if (vt->cy == vt->region_top)
ea7edec4 729 spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, 1);
0e80a2f3
DM
730 else if (vt->cy > 0) {
731 vt->cy--;
732 }
733 break;
734 case '>':
735 /* numeric keypad - ignored */
736 break;
737 case '=':
738 /* appl. keypad - ignored */
739 break;
740 default:
a3fd8291 741 DPRINTF(1, "got unhandled ESC%c %d", ch, ch);
0e80a2f3
DM
742 break;
743 }
744 break;
745 case ESnonstd: /* Operating System Controls */
746 vt->tty_state = ESnormal;
747
748 switch (ch) {
749 case 'P': /* palette escape sequence */
750 for(i = 0; i < MAX_ESC_PARAMS; i++) {
751 vt->esc_buf[i] = 0;
752 }
22e5ba02 753
0e80a2f3
DM
754 vt->esc_count = 0;
755 vt->tty_state = ESpalette;
756 break;
757 case 'R': /* reset palette */
758 // fixme: reset_palette(vc);
759 break;
760 case '0':
761 case '1':
762 case '2':
763 case '4':
764 vt->osc_cmd = ch;
765 vt->osc_textbuf[0] = 0;
766 vt->tty_state = ESosc1;
767 break;
768 default:
a3fd8291 769 DPRINTF(1, "got unhandled OSC %c", ch);
0e80a2f3
DM
770 vt->tty_state = ESnormal;
771 break;
772 }
773 break;
774 case ESosc1:
775 vt->tty_state = ESnormal;
776 if (ch == ';') {
777 vt->tty_state = ESosc2;
778 } else {
a3fd8291 779 DPRINTF(1, "got illegal OSC sequence");
0e80a2f3
DM
780 }
781 break;
782 case ESosc2:
783 if (ch != 0x9c && ch != 7) {
784 int i = 0;
785 while (vt->osc_textbuf[i]) i++;
786 vt->osc_textbuf[i++] = ch;
787 vt->osc_textbuf[i] = 0;
788 } else {
a3fd8291 789 DPRINTF(1, "OSC:%c:%s", vt->osc_cmd, vt->osc_textbuf);
0e80a2f3
DM
790 vt->tty_state = ESnormal;
791 }
792 break;
793 case ESpalette:
794 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')
795 || (ch >= 'a' && ch <= 'f')) {
796 vt->esc_buf[vt->esc_count++] = (ch > '9' ? (ch & 0xDF) - 'A' + 10 : ch - '0');
797 if (vt->esc_count == 7) {
798 // fixme: this does not work - please test
799 /*
800 rfbColourMap *cmap =&vt->screen->colourMap;
801
802 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
803 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
804 cmap->data.bytes[i++] += vt->esc_buf[j++];
805 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
806 cmap->data.bytes[i++] += vt->esc_buf[j++];
807 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
808 cmap->data.bytes[i] += vt->esc_buf[j];
809 */
810 //set_palette(vc); ?
424ef03c 811
0e80a2f3
DM
812 vt->tty_state = ESnormal;
813 }
814 } else
815 vt->tty_state = ESnormal;
816 break;
817 case ESsquare:
818 for(i = 0; i < MAX_ESC_PARAMS; i++) {
819 vt->esc_buf[i] = 0;
820 }
22e5ba02 821
0e80a2f3
DM
822 vt->esc_count = 0;
823 vt->esc_has_par = 0;
824 vt->tty_state = ESgetpars;
424ef03c 825
0e80a2f3
DM
826 if (ch == '>') {
827 vt->tty_state = ESidquery;
828 break;
829 }
22e5ba02 830
0e80a2f3
DM
831 if ((vt->esc_ques = (ch == '?'))) {
832 break;
833 }
834 case ESgetpars:
835 if (ch >= '0' && ch <= '9') {
836 vt->esc_has_par = 1;
837 if (vt->esc_count < MAX_ESC_PARAMS) {
838 vt->esc_buf[vt->esc_count] = vt->esc_buf[vt->esc_count] * 10 + ch - '0';
839 }
840 break;
841 } else if (ch == ';') {
842 vt->esc_count++;
843 break;
a0579497 844 } else {
0e80a2f3
DM
845 if (vt->esc_has_par) {
846 vt->esc_count++;
847 }
848 vt->tty_state = ESgotpars;
849 }
850 case ESgotpars:
424ef03c 851
0e80a2f3
DM
852 vt->tty_state = ESnormal;
853
854 char *qes = vt->esc_ques ? "?" : "";
855
856 if (debug) {
a3fd8291 857 debug_print_escape_buffer(vt, __func__, "", qes, ch);
a0579497 858 }
22e5ba02 859
0e80a2f3
DM
860 switch (ch) {
861 case 'h':
ded68f44 862 spiceterm_set_mode(vt, 1);
0e80a2f3
DM
863 break;
864 case 'l':
ded68f44 865 spiceterm_set_mode(vt, 0);
0e80a2f3
DM
866 break;
867 case 'm':
868 if (!vt->esc_count) {
869 vt->esc_count++; // default parameter 0
870 }
ea7edec4 871 spiceterm_csi_m(vt);
0e80a2f3
DM
872 break;
873 case 'n':
874 /* report cursor position */
875 /* TODO: send ESC[row;colR */
876 break;
877 case 'A':
878 /* move cursor up */
879 if (vt->esc_buf[0] == 0) {
880 vt->esc_buf[0] = 1;
881 }
882 vt->cy -= vt->esc_buf[0];
883 if (vt->cy < 0) {
884 vt->cy = 0;
885 }
886 break;
887 case 'B':
888 case 'e':
889 /* move cursor down */
890 if (vt->esc_buf[0] == 0) {
891 vt->esc_buf[0] = 1;
892 }
893 vt->cy += vt->esc_buf[0];
894 if (vt->cy >= vt->height) {
895 vt->cy = vt->height - 1;
896 }
897 break;
898 case 'C':
899 case 'a':
900 /* move cursor right */
901 if (vt->esc_buf[0] == 0) {
902 vt->esc_buf[0] = 1;
903 }
904 vt->cx += vt->esc_buf[0];
905 if (vt->cx >= vt->width) {
906 vt->cx = vt->width - 1;
907 }
908 break;
909 case 'D':
910 /* move cursor left */
911 if (vt->esc_buf[0] == 0) {
912 vt->esc_buf[0] = 1;
913 }
914 vt->cx -= vt->esc_buf[0];
915 if (vt->cx < 0) {
916 vt->cx = 0;
917 }
918 break;
919 case 'G':
920 case '`':
921 /* move cursor to column */
ea7edec4 922 spiceterm_gotoxy(vt, vt->esc_buf[0] - 1, vt->cy);
0e80a2f3
DM
923 break;
924 case 'd':
925 /* move cursor to row */
ea7edec4 926 spiceterm_gotoxy(vt, vt->cx , vt->esc_buf[0] - 1);
0e80a2f3
DM
927 break;
928 case 'f':
929 case 'H':
930 /* move cursor to row, column */
ea7edec4 931 spiceterm_gotoxy(vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1);
0e80a2f3
DM
932 break;
933 case 'J':
934 switch (vt->esc_buf[0]) {
935 case 0:
936 /* clear to end of screen */
937 for (y = vt->cy; y < vt->height; y++) {
938 for (x = 0; x < vt->width; x++) {
939 if (y == vt->cy && x < vt->cx) {
940 continue;
941 }
942 spiceterm_clear_xy (vt, x, y);
943 }
944 }
945 break;
946 case 1:
947 /* clear from beginning of screen */
948 for (y = 0; y <= vt->cy; y++) {
949 for (x = 0; x < vt->width; x++) {
950 if (y == vt->cy && x > vt->cx) {
951 break;
952 }
ea7edec4 953 spiceterm_clear_xy(vt, x, y);
0e80a2f3
DM
954 }
955 }
956 break;
957 case 2:
958 /* clear entire screen */
e420a6d9 959 spiceterm_clear_screen(vt);
0e80a2f3
DM
960 break;
961 }
962 break;
963 case 'K':
964 switch (vt->esc_buf[0]) {
965 case 0:
966 /* clear to eol */
967 for(x = vt->cx; x < vt->width; x++) {
ea7edec4 968 spiceterm_clear_xy(vt, x, vt->cy);
0e80a2f3
DM
969 }
970 break;
971 case 1:
972 /* clear from beginning of line */
973 for (x = 0; x <= vt->cx; x++) {
ea7edec4 974 spiceterm_clear_xy(vt, x, vt->cy);
0e80a2f3
DM
975 }
976 break;
977 case 2:
978 /* clear entire line */
979 for(x = 0; x < vt->width; x++) {
ea7edec4 980 spiceterm_clear_xy(vt, x, vt->cy);
0e80a2f3
DM
981 }
982 break;
983 }
984 break;
985 case 'L':
986 /* insert line */
987 c = vt->esc_buf[0];
424ef03c 988
0e80a2f3
DM
989 if (c > vt->height - vt->cy)
990 c = vt->height - vt->cy;
991 else if (!c)
992 c = 1;
993
ea7edec4 994 spiceterm_scroll_down(vt, vt->cy, vt->region_bottom, c);
0e80a2f3
DM
995 break;
996 case 'M':
997 /* delete line */
998 c = vt->esc_buf[0];
424ef03c 999
0e80a2f3
DM
1000 if (c > vt->height - vt->cy)
1001 c = vt->height - vt->cy;
1002 else if (!c)
1003 c = 1;
1004
ea7edec4 1005 spiceterm_scroll_up(vt, vt->cy, vt->region_bottom, c, 1);
0e80a2f3
DM
1006 break;
1007 case 'T':
1008 /* scroll down */
1009 c = vt->esc_buf[0];
1010 if (!c) c = 1;
ea7edec4 1011 spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, c);
0e80a2f3
DM
1012 break;
1013 case 'S':
1014 /* scroll up */
1015 c = vt->esc_buf[0];
1016 if (!c) c = 1;
ea7edec4 1017 spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, c, 1);
0e80a2f3
DM
1018 break;
1019 case 'P':
1020 /* delete c character */
1021 c = vt->esc_buf[0];
424ef03c 1022
0e80a2f3
DM
1023 if (c > vt->width - vt->cx)
1024 c = vt->width - vt->cx;
1025 else if (!c)
1026 c = 1;
1027
1028 for (x = vt->cx; x < vt->width - c; x++) {
1029 int y1 = (vt->y_base + vt->cy) % vt->total_height;
1030 TextCell *dst = &vt->cells[y1 * vt->width + x];
1031 TextCell *src = dst + c;
1032 *dst = *src;
ea7edec4 1033 spiceterm_update_xy(vt, x + c, vt->cy);
0e80a2f3
DM
1034 src->ch = ' ';
1035 src->attrib = vt->default_attrib;
ea7edec4 1036 spiceterm_update_xy(vt, x, vt->cy);
0e80a2f3
DM
1037 }
1038 break;
1039 case 's':
1040 /* save cursor position */
ea7edec4 1041 spiceterm_save_cursor(vt);
0e80a2f3
DM
1042 break;
1043 case 'u':
1044 /* restore cursor position */
ea7edec4 1045 spiceterm_restore_cursor(vt);
0e80a2f3
DM
1046 break;
1047 case 'X':
1048 /* erase c characters */
1049 c = vt->esc_buf[0];
1050 if (!c) c = 1;
424ef03c 1051
0e80a2f3 1052 if (c > (vt->width - vt->cx)) c = vt->width - vt->cx;
22e5ba02 1053
0e80a2f3 1054 for(i = 0; i < c; i++) {
ea7edec4 1055 spiceterm_clear_xy(vt, vt->cx + i, vt->cy);
0e80a2f3
DM
1056 }
1057 break;
1058 case '@':
1059 /* insert c character */
1060 c = vt->esc_buf[0];
1061 if (c > (vt->width - vt->cx)) {
1062 c = vt->width - vt->cx;
1063 }
1064 if (!c) c = 1;
424ef03c 1065
0e80a2f3
DM
1066 for (x = vt->width - c; x >= vt->cx; x--) {
1067 int y1 = (vt->y_base + vt->cy) % vt->total_height;
1068 TextCell *src = &vt->cells[y1 * vt->width + x];
1069 TextCell *dst = src + c;
1070 *dst = *src;
1071 spiceterm_update_xy (vt, x + c, vt->cy);
1072 src->ch = ' ';
1073 src->attrib = vt->cur_attrib;
ea7edec4 1074 spiceterm_update_xy(vt, x, vt->cy);
0e80a2f3 1075 }
22e5ba02 1076
0e80a2f3
DM
1077 break;
1078 case 'r':
1079 /* set region */
1080 if (!vt->esc_buf[0])
1081 vt->esc_buf[0]++;
1082 if (!vt->esc_buf[1])
1083 vt->esc_buf[1] = vt->height;
1084 /* Minimum allowed region is 2 lines */
1085 if (vt->esc_buf[0] < vt->esc_buf[1] &&
1086 vt->esc_buf[1] <= vt->height) {
1087 vt->region_top = vt->esc_buf[0] - 1;
1088 vt->region_bottom = vt->esc_buf[1];
1089 vt->cx = 0;
1090 vt->cy = vt->region_top;
a3fd8291 1091 DPRINTF(1, "set region %d %d", vt->region_top, vt->region_bottom);
0e80a2f3 1092 }
22e5ba02 1093
0e80a2f3
DM
1094 break;
1095 default:
1096 if (debug) {
a3fd8291 1097 debug_print_escape_buffer(vt, __func__, " unhandled escape", qes, ch);
0e80a2f3
DM
1098 }
1099 break;
1100 }
1101 vt->esc_ques = 0;
1102 break;
1103 case ESsetG0: // Set G0
1104 vt->tty_state = ESnormal;
1105
1106 if (ch == '0')
1107 vt->g0enc = GRAF_MAP;
1108 else if (ch == 'B')
1109 vt->g0enc = LAT1_MAP;
1110 else if (ch == 'U')
1111 vt->g0enc = IBMPC_MAP;
1112 else if (ch == 'K')
1113 vt->g0enc = USER_MAP;
424ef03c 1114
0e80a2f3
DM
1115 if (vt->charset == 0)
1116 vt->cur_enc = vt->g0enc;
1117
1118 break;
1119 case ESsetG1: // Set G1
1120 vt->tty_state = ESnormal;
1121
1122 if (ch == '0')
1123 vt->g1enc = GRAF_MAP;
1124 else if (ch == 'B')
1125 vt->g1enc = LAT1_MAP;
1126 else if (ch == 'U')
1127 vt->g1enc = IBMPC_MAP;
1128 else if (ch == 'K')
1129 vt->g1enc = USER_MAP;
1130
1131 if (vt->charset == 1)
1132 vt->cur_enc = vt->g1enc;
1133
1134 break;
1135 case ESidquery: // vt100 query id
1136 vt->tty_state = ESnormal;
1137
1138 if (ch == 'c') {
a3fd8291 1139 DPRINTF(1, "ESC[>c Query term ID");
ea7edec4 1140 spiceterm_respond_esc(vt, TERMIDCODE);
ded68f44 1141 }
0e80a2f3
DM
1142 break;
1143 case ESpercent:
1144 vt->tty_state = ESnormal;
1145 switch (ch) {
1146 case '@': /* defined in ISO 2022 */
1147 vt->utf8 = 0;
1148 break;
1149 case 'G': /* prelim official escape code */
1150 case '8': /* retained for compatibility */
1151 vt->utf8 = 1;
1152 break;
1153 }
1154 break;
1155 default: // ESnormal
1156 vt->tty_state = ESnormal;
1157
1158 switch(ch) {
1159 case 0:
1160 break;
1161 case 7: /* alert aka. bell */
1162 // fixme:
1163 //rfbSendBell(vt->screen);
1164 break;
1165 case 8: /* backspace */
1166 if (vt->cx > 0)
1167 vt->cx--;
1168 break;
1169 case 9: /* tabspace */
1170 if (vt->cx + (8 - (vt->cx % 8)) > vt->width) {
1171 vt->cx = 0;
ea7edec4 1172 spiceterm_put_lf(vt);
0e80a2f3
DM
1173 } else {
1174 vt->cx = vt->cx + (8 - (vt->cx % 8));
1175 }
1176 break;
1177 case 10: /* LF,*/
1178 case 11: /* VT */
1179 case 12: /* FF */
ea7edec4 1180 spiceterm_put_lf(vt);
0e80a2f3
DM
1181 break;
1182 case 13: /* carriage return */
1183 vt->cx = 0;
1184 break;
1185 case 14:
1186 /* SI (shift in), select character set 1 */
1187 vt->charset = 1;
1188 vt->cur_enc = vt->g1enc;
1189 /* fixme: display controls = 1 */
1190 break;
1191 case 15:
1192 /* SO (shift out), select character set 0 */
1193 vt->charset = 0;
1194 vt->cur_enc = vt->g0enc;
1195 /* fixme: display controls = 0 */
1196 break;
1197 case 27: /* esc */
1198 vt->tty_state = ESesc;
1199 break;
1200 case 127: /* delete */
1201 /* ignore */
1202 break;
1203 case 128+27: /* csi */
1204 vt->tty_state = ESsquare;
1205 break;
1206 default:
1207 if (vt->cx >= vt->width) {
1208 /* line wrap */
1209 vt->cx = 0;
ea7edec4 1210 spiceterm_put_lf(vt);
0e80a2f3
DM
1211 }
1212
1213 int y1 = (vt->y_base + vt->cy) % vt->total_height;
1214 TextCell *c = &vt->cells[y1*vt->width + vt->cx];
1215 c->attrib = vt->cur_attrib;
1216 c->ch = ch;
ea7edec4 1217 spiceterm_update_xy(vt, vt->cx, vt->cy);
0e80a2f3
DM
1218 vt->cx++;
1219 break;
1220 }
1221 break;
22e5ba02 1222 }
22e5ba02
DM
1223}
1224
1225static int
ea7edec4 1226spiceterm_puts(spiceTerm *vt, const char *buf, int len)
22e5ba02 1227{
b052e9c7 1228 gunichar2 tc;
22e5ba02 1229
ea7edec4 1230 spiceterm_show_cursor(vt, 0);
22e5ba02
DM
1231
1232 while (len) {
0e80a2f3
DM
1233 unsigned char c = *buf;
1234 len--;
1235 buf++;
1236
1237 if (vt->tty_state != ESnormal) {
1238 // never translate escape sequence
1239 tc = c;
1240 } else if (vt->utf8 && !vt->cur_enc) {
1241
1242 if(c & 0x80) { // utf8 multi-byte sequence
1243
1244 if (vt->utf_count > 0 && (c & 0xc0) == 0x80) {
1245 // inside UTF8 sequence
1246 vt->utf_char = (vt->utf_char << 6) | (c & 0x3f);
1247 vt->utf_count--;
1248 if (vt->utf_count == 0) {
1249 tc = vt->utf_char;
1250 } else {
1251 continue;
1252 }
1253 } else {
1254 // first char of a UTF8 sequence
1255 if ((c & 0xe0) == 0xc0) {
1256 vt->utf_count = 1;
1257 vt->utf_char = (c & 0x1f);
1258 } else if ((c & 0xf0) == 0xe0) {
1259 vt->utf_count = 2;
1260 vt->utf_char = (c & 0x0f);
1261 } else if ((c & 0xf8) == 0xf0) {
1262 vt->utf_count = 3;
1263 vt->utf_char = (c & 0x07);
1264 } else if ((c & 0xfc) == 0xf8) {
1265 vt->utf_count = 4;
1266 vt->utf_char = (c & 0x03);
1267 } else if ((c & 0xfe) == 0xfc) {
1268 vt->utf_count = 5;
1269 vt->utf_char = (c & 0x01);
1270 } else
1271 vt->utf_count = 0;
1272
1273 continue;
1274 }
1275 } else {
1276 // utf8 single byte
1277 tc = c;
1278 vt->utf_count = 0;
1279 }
22e5ba02 1280
0e80a2f3
DM
1281 } else {
1282 // never translate controls
1283 if (c >= 32 && c != 127 && c != (128+27)) {
1284 tc = translations[vt->cur_enc][c & 0x0ff];
1285 } else {
1286 tc = c;
1287 }
1288 }
424ef03c 1289
ea7edec4 1290 spiceterm_putchar(vt, tc);
22e5ba02
DM
1291 }
1292
ea7edec4 1293 spiceterm_show_cursor(vt, 1);
424ef03c 1294
22e5ba02
DM
1295 return len;
1296}
1297
22e5ba02 1298void
5cba5790
DM
1299spiceterm_update_watch_mask(spiceTerm *vt, gboolean writable)
1300{
1301 g_assert(vt != NULL);
1302
1303 int mask = SPICE_WATCH_EVENT_READ;
1304
1305 if (writable) {
1306 mask |= SPICE_WATCH_EVENT_WRITE;
1307 }
1308
1309 vt->screen->core->watch_update_mask(vt->screen->mwatch, mask);
1310}
1311
22e5ba02 1312static void
ded68f44 1313mouse_report(spiceTerm *vt, int butt, int mrx, int mry)
22e5ba02 1314{
ded68f44
DM
1315 char buf[8];
1316
ea7edec4 1317 sprintf(buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
ded68f44 1318 (char)('!' + mry));
22e5ba02 1319
ded68f44
DM
1320 spiceterm_respond_esc(vt, buf);
1321
5cba5790 1322 spiceterm_update_watch_mask(vt, TRUE);
22e5ba02
DM
1323}
1324
1d11aa12
DM
1325static void
1326spiceterm_respond_unichar2(spiceTerm *vt, gunichar2 uc)
22e5ba02 1327{
1d11aa12
DM
1328 if (vt->utf8) {
1329 gchar buf[10];
1330 gint len = g_unichar_to_utf8(uc, buf);
1331
1332 if (len > 0) {
f424be98 1333 spiceterm_respond_data(vt, len, (uint8_t *)buf);
06a07275
DM
1334 }
1335 } else {
f424be98
DM
1336 uint8_t buf[1] = { (uint8_t)uc };
1337 spiceterm_respond_data(vt, 1, buf);
06a07275 1338 }
55cfb9f8
DM
1339}
1340
f424be98 1341void
48b01506
DM
1342spiceterm_clear_selection(spiceTerm *vt)
1343{
1344 DPRINTF(1, "mark_active = %d", vt->mark_active);
1345
1346 vt->mark_active = 0;
1347 if (vt->selection) free (vt->selection);
1348 vt->selection = NULL;
1349
1350 spiceterm_unselect_all(vt);
1351}
1352
f424be98 1353void
eddeb865
DM
1354spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y, uint32_t buttons)
1355{
1356 DPRINTF(1, "mask=%08x x=%d y=%d", buttons, x ,y);
1357
1358 static int last_mask = 0;
1359 static int sel_start_pos = 0;
1360 static int sel_end_pos = 0;
1361 static int button2_released = 1;
1362
1363 int i;
1364 int cx = x/8;
1365 int cy = y/16;
1366
1367 if (cx < 0) cx = 0;
1368 if (cx >= vt->width) cx = vt->width - 1;
1369 if (cy < 0) cy = 0;
1370 if (cy >= vt->height) cy = vt->height - 1;
1371
1372 if (vt->report_mouse && buttons != last_mask) {
1373 last_mask = buttons;
1374 if (buttons & 2) {
1375 mouse_report(vt, 0, cx, cy);
1376 }
1377 if (buttons & 4) {
1378 mouse_report (vt, 1, cx, cy);
1379 }
1380 if (buttons & 8) {
1381 mouse_report(vt, 2, cx, cy);
1382 }
1383 if(!buttons) {
1384 mouse_report(vt, 3, cx, cy);
1385 }
1386 }
1387
1388 if (buttons & 4) {
1389
1390 if(button2_released) {
1391
f424be98 1392 if (vdagent_owns_clipboard(vt)) {
eddeb865
DM
1393 if (vt->selection) {
1394 int i;
1395 for(i = 0; i < vt->selection_len; i++) {
1396 spiceterm_respond_unichar2(vt, vt->selection[i]);
1397 }
1398 spiceterm_update_watch_mask(vt, TRUE);
1399 if (vt->y_displ != vt->y_base) {
1400 vt->y_displ = vt->y_base;
1401 spiceterm_refresh(vt);
1402 }
1403 }
1404 } else {
f424be98 1405 vdagent_request_clipboard(vt);
eddeb865
DM
1406 }
1407 }
1408
1409 button2_released = 0;
1410 } else {
1411 button2_released = 1;
1412 }
1413
1414 if (buttons & 2) {
1415 int pos = cy*vt->width + cx;
1416
1417 // code borrowed from libvncserver (VNCconsole.c)
1418
1419 if (!vt->mark_active) {
1420
1421 spiceterm_unselect_all(vt);
1422
1423 vt->mark_active = 1;
1424 sel_start_pos = sel_end_pos = pos;
1425 spiceterm_toggle_marked_cell(vt, pos);
1426
1427 } else {
1428
1429 if (pos != sel_end_pos) {
1430 if (pos > sel_end_pos) {
1431 cx = sel_end_pos; cy=pos;
1432 } else {
1433 cx=pos; cy=sel_end_pos;
1434 }
1435
1436 if (cx < sel_start_pos) {
1437 if (cy < sel_start_pos) cy--;
1438 } else {
1439 cx++;
1440 }
1441
1442 while (cx <= cy) {
1443 spiceterm_toggle_marked_cell(vt, cx);
1444 cx++;
1445 }
1446
1447 sel_end_pos = pos;
1448 }
1449 }
1450
1451 } else if (vt->mark_active) {
1452 vt->mark_active = 0;
1453
1454 if (sel_start_pos > sel_end_pos) {
1455 int tmp = sel_start_pos - 1;
1456 sel_start_pos = sel_end_pos;
1457 sel_end_pos = tmp;
1458 }
1459
1460 int len = sel_end_pos - sel_start_pos + 1;
1461
1462 if (vt->selection) free (vt->selection);
1463 vt->selection = (gunichar2 *)malloc (len*sizeof(gunichar2));
1464 vt->selection_len = len;
1465
1466 for (i = 0; i < len; i++) {
1467 int pos = sel_start_pos + i;
1468 int x = pos % vt->width;
1469 int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height;
1470 TextCell *c = &vt->cells[y1*vt->width + x];
1471 vt->selection[i] = c->ch;
1472 c++;
1473 }
1474
1475 DPRINTF(1, "selection length = %d", vt->selection_len);
1476
f424be98 1477 vdagent_grab_clipboard(vt);
69a7370c 1478 }
60b9ef63
DM
1479}
1480
f424be98 1481void
8f53ae81 1482init_spiceterm(spiceTerm *vt, uint32_t width, uint32_t height)
22e5ba02 1483{
0e80a2f3 1484 int i;
22e5ba02 1485
8f53ae81
DM
1486 g_assert(vt != NULL);
1487 g_assert(vt->screen != NULL);
22e5ba02 1488
8f53ae81
DM
1489 vt->width = width / 8;
1490 vt->height = height / 16;
22e5ba02 1491
0e80a2f3
DM
1492 vt->total_height = vt->height * 20;
1493 vt->scroll_height = 0;
1494 vt->y_base = 0;
1495 vt->y_displ = 0;
22e5ba02 1496
0e80a2f3
DM
1497 vt->region_top = 0;
1498 vt->region_bottom = vt->height;
22e5ba02 1499
0e80a2f3
DM
1500 vt->g0enc = LAT1_MAP;
1501 vt->g1enc = GRAF_MAP;
1502 vt->cur_enc = vt->g0enc;
1503 vt->charset = 0;
22e5ba02 1504
0e80a2f3
DM
1505 /* default text attributes */
1506 vt->default_attrib.bold = 0;
1507 vt->default_attrib.uline = 0;
1508 vt->default_attrib.blink = 0;
1509 vt->default_attrib.invers = 0;
1510 vt->default_attrib.unvisible = 0;
1511 vt->default_attrib.fgcol = 7;
1512 vt->default_attrib.bgcol = 0;
22e5ba02 1513
0e80a2f3 1514 vt->cur_attrib = vt->default_attrib;
22e5ba02 1515
8f53ae81
DM
1516 if (vt->cells) {
1517 vt->cx = 0;
1518 vt->cy = 0;
1519 vt->cx_saved = 0;
1520 vt->cy_saved = 0;
1521 g_free(vt->cells);
1522 }
1523
0e80a2f3 1524 vt->cells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->total_height);
424ef03c 1525
0e80a2f3
DM
1526 for (i = 0; i < vt->width*vt->total_height; i++) {
1527 vt->cells[i].ch = ' ';
1528 vt->cells[i].attrib = vt->default_attrib;
1529 }
8f53ae81
DM
1530
1531 if (vt->altcells) {
1532 g_free(vt->altcells);
1533 }
22e5ba02 1534
0e80a2f3 1535 vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height);
8f53ae81
DM
1536}
1537
f424be98 1538void
8f53ae81
DM
1539spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height)
1540{
8a3547be
DM
1541 width = (width/8)*8;
1542 height = (height/16)*16;
1543
8f53ae81
DM
1544 if (vt->screen->width == width && vt->screen->height == height) {
1545 return;
1546 }
1547
8a3547be
DM
1548 DPRINTF(0, "width=%u height=%u", width, height);
1549
8f53ae81
DM
1550 spice_screen_resize(vt->screen, width, height);
1551
1552 init_spiceterm(vt, width, height);
1553
1554 struct winsize dimensions;
1555 dimensions.ws_col = vt->width;
1556 dimensions.ws_row = vt->height;
1557
1558 ioctl(vt->pty, TIOCSWINSZ, &dimensions);
1559}
1560
ea7edec4
DM
1561static gboolean
1562master_error_callback(GIOChannel *channel, GIOCondition condition,
7ccbd303
DM
1563 gpointer data)
1564{
1565 //spiceTerm *vt = (spiceTerm *)data;
1566
1567 DPRINTF(1, "condition %d", condition);
1568
1569 exit(0);
1570
1571 return FALSE;
1572}
1573
ea7edec4 1574static void
424ef03c 1575master_watch(int master, int event, void *opaque)
22e5ba02 1576{
d6a5f5a9 1577 spiceTerm *vt = (spiceTerm *)opaque;
c13b3361 1578 int c;
22e5ba02 1579
22e5ba02
DM
1580 // fixme: if (!vt->mark_active) {
1581
1582 if (event == SPICE_WATCH_EVENT_READ) {
1583 char buffer[1024];
22e5ba02
DM
1584 while ((c = read(master, buffer, 1024)) == -1) {
1585 if (errno != EAGAIN) break;
1586 }
1587 if (c == -1) {
c13b3361 1588 perror("master pipe read error"); // fixme
22e5ba02 1589 }
d6a5f5a9 1590 spiceterm_puts (vt, buffer, c);
22e5ba02
DM
1591 } else {
1592 if (vt->ibuf_count > 0) {
a3fd8291 1593 DPRINTF(1, "write input %x %d", vt->ibuf[0], vt->ibuf_count);
c13b3361
DM
1594 if ((c = write (master, vt->ibuf, vt->ibuf_count)) >= 0) {
1595 if (c == vt->ibuf_count) {
ea7edec4 1596 vt->ibuf_count = 0;
c13b3361
DM
1597 } else if (c > 0) {
1598 // not all data written
1599 memmove(vt->ibuf, vt->ibuf + c, vt->ibuf_count - c);
ea7edec4 1600 vt->ibuf_count -= c;
c13b3361
DM
1601 } else {
1602 // nothing written -ignore and try later
1603 }
1604 } else {
1605 perror("master pipe write error");
1606 }
1607 }
1608 if (vt->ibuf_count == 0) {
1609 spiceterm_update_watch_mask(vt, FALSE);
22e5ba02 1610 }
22e5ba02
DM
1611 }
1612}
1613
1614int
1615main (int argc, char** argv)
1616{
0e80a2f3
DM
1617 int i;
1618 char **cmdargv = NULL;
1619 char *command = "/bin/bash"; // execute normal shell as default
1620 int pid;
1621 int master;
1622 char ptyname[1024];
1623 struct winsize dimensions;
1624
1625 g_thread_init(NULL);
1626
1627 for (i = 1; i < argc; i++) {
1628 if (!strcmp (argv[i], "-c")) {
1629 command = argv[i+1];
1630 cmdargv = &argv[i+1];
1631 argc = i;
1632 argv[i] = NULL;
1633 break;
1634 }
22e5ba02 1635 }
22e5ba02 1636
0e80a2f3 1637 if (0) print_usage(NULL); // fixme:
abc13312 1638
8a3547be 1639 spiceTerm *vt = create_spiceterm (argc, argv, 744, 400, 10);
22e5ba02 1640
0e80a2f3 1641 setlocale(LC_ALL, ""); // set from environment
22e5ba02 1642
0e80a2f3 1643 char *ctype = setlocale (LC_CTYPE, NULL); // query LC_CTYPE
22e5ba02 1644
0e80a2f3
DM
1645 // fixme: ist there a standard way to detect utf8 mode ?
1646 if (strcasestr (ctype, ".utf-8")||strcasestr (ctype, ".utf8")) {
1647 vt->utf8 = 1;
1648 }
22e5ba02 1649
0e80a2f3
DM
1650 dimensions.ws_col = vt->width;
1651 dimensions.ws_row = vt->height;
22e5ba02 1652
ded68f44 1653 setenv("TERM", TERM, 1);
22e5ba02 1654
a3fd8291 1655 DPRINTF(1, "execute %s", command);
22e5ba02 1656
0e80a2f3
DM
1657 pid = forkpty (&master, ptyname, NULL, &dimensions);
1658 if(!pid) {
22e5ba02 1659
0e80a2f3
DM
1660 // install default signal handlers
1661 signal (SIGQUIT, SIG_DFL);
1662 signal (SIGTERM, SIG_DFL);
1663 signal (SIGINT, SIG_DFL);
22e5ba02 1664
0e80a2f3
DM
1665 if (cmdargv) {
1666 execvp (command, cmdargv);
1667 } else {
1668 execlp (command, command, NULL);
1669 }
1670 perror ("Error: exec failed\n");
1671 exit (-1); // should not be reached
1672 } else if (pid == -1) {
1673 perror ("Error: fork failed\n");
1674 exit (-1);
22e5ba02 1675 }
22e5ba02 1676
8f53ae81
DM
1677 vt->pty = master;
1678
7ccbd303
DM
1679 /* watch for errors - we need to use glib directly because spice
1680 * does not have SPICE_WATCH_EVENT for this */
1681 GIOChannel *channel = g_io_channel_unix_new(master);
1682 g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL);
1683 g_io_channel_set_encoding(channel, NULL, NULL);
1684 g_io_add_watch(channel, G_IO_ERR|G_IO_HUP, master_error_callback, vt);
ea7edec4 1685
0e80a2f3
DM
1686 vt->screen->mwatch = vt->screen->core->watch_add(
1687 master, SPICE_WATCH_EVENT_READ /* |SPICE_WATCH_EVENT_WRITE */,
1688 master_watch, vt);
22e5ba02 1689
0e80a2f3 1690 basic_event_loop_mainloop();
22e5ba02 1691
0e80a2f3
DM
1692 kill (pid, 9);
1693 int status;
1694 waitpid(pid, &status, 0);
22e5ba02 1695
0e80a2f3 1696 exit (0);
22e5ba02 1697}