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