]> git.proxmox.com Git - spiceterm.git/blob - spiceterm.c
move spice input/vdagent related code to input.c
[spiceterm.git] / spiceterm.c
1 /*
2
3 Copyright (C) 2013 Proxmox Server Solutions GmbH
4
5 Copyright: spiceterm is under GNU GPL, the GNU General Public License.
6
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.
10
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.
15
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.
20
21 Author: Dietmar Maurer <dietmar@proxmox.com>
22
23 Note: most of the code here is copied from vncterm (which is
24 also written by me).
25
26 */
27
28 #define _GNU_SOURCE
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <sys/types.h>
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"
46
47 #include <glib.h>
48 #include <spice.h>
49 #include <spice/enums.h>
50 #include <spice/macros.h>
51 #include <spice/qxl_dev.h>
52
53 #include <gdk/gdkkeysyms.h>
54
55 #include "event_loop.h"
56 #include "translations.h"
57
58 static int debug = 0;
59
60 #define DPRINTF(x, format, ...) { \
61 if (x <= debug) { \
62 printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \
63 } \
64 }
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) { \
71 fprintf(stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
72 print_usage(NULL); \
73 exit(1); \
74 }
75
76 /* these colours are from linux kernel drivers/char/vt.c */
77
78 unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
79 8,12,10,14, 9,13,11,15 };
80
81 static void
82 print_usage(const char *msg)
83 {
84 if (msg) { fprintf(stderr, "ERROR: %s\n", msg); }
85 fprintf(stderr, "USAGE: spiceterm [spiceopts] [-c command [args]]\n");
86 }
87
88 static void
89 draw_char_at(spiceTerm *vt, int x, int y, gunichar2 ch, TextAttributes attrib)
90 {
91 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) {
92 return;
93 }
94
95 spice_screen_draw_char(vt->screen, x, y, ch, attrib);
96 }
97
98 static void
99 spiceterm_update_xy(spiceTerm *vt, int x, int y)
100 {
101 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
102
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];
110 draw_char_at(vt, x, y2, c->ch, c->attrib);
111 }
112 }
113
114 static void
115 spiceterm_clear_xy(spiceTerm *vt, int x, int y)
116 {
117 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
118
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
131 draw_char_at(vt, x, y, c->ch, c->attrib);
132 }
133 }
134
135 void
136 spiceterm_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
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);
150 }
151 }
152
153 static void
154 spiceterm_show_cursor(spiceTerm *vt, int show)
155 {
156 int x = vt->cx;
157 if (x >= vt->width) {
158 x = vt->width - 1;
159 }
160
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 }
166
167 if (y < vt->height) {
168
169 TextCell *c = &vt->cells[y1 * vt->width + x];
170
171 if (show) {
172 TextAttributes attrib = vt->default_attrib;
173 attrib.invers = !(attrib.invers); /* invert fg and bg */
174 draw_char_at(vt, x, y, c->ch, attrib);
175 } else {
176 draw_char_at(vt, x, y, c->ch, c->attrib);
177 }
178 }
179 }
180
181 void
182 spiceterm_refresh(spiceTerm *vt)
183 {
184 int x, y, y1;
185
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++) {
190 draw_char_at(vt, x, y, c->ch, c->attrib);
191 c++;
192 }
193 if (++y1 == vt->total_height)
194 y1 = 0;
195 }
196
197 spiceterm_show_cursor(vt, 1);
198 }
199
200 static void
201 spiceterm_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
222 void
223 spiceterm_unselect_all(spiceTerm *vt)
224 {
225 int x, y, y1;
226
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++;
238 }
239 if (++y1 == vt->total_height)
240 y1 = 0;
241 }
242 }
243
244 static void
245 spiceterm_scroll_down(spiceTerm *vt, int top, int bottom, int lines)
246 {
247 if ((top + lines) >= bottom) {
248 lines = bottom - top -1;
249 }
250
251 if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
252 return;
253 }
254
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;
259
260 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof(TextCell));
261 }
262
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 }
272
273 int h = lines * 16;
274 int y0 = top*16;
275 int y1 = y0 + h;
276 int y2 = bottom*16;
277
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);
280 }
281
282 static void
283 spiceterm_scroll_up(spiceTerm *vt, int top, int bottom, int lines, int moveattr)
284 {
285 if ((top + lines) >= bottom) {
286 lines = bottom - top - 1;
287 }
288
289 if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
290 return;
291 }
292
293
294 int h = lines * 16;
295 int y0 = top*16;
296 int y1 = (top + lines)*16;
297 int y2 = bottom*16;
298
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);
301
302 if (!moveattr) {
303 return;
304 }
305
306 // move attributes
307
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;
312
313 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof(TextCell));
314 }
315
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 }
324 }
325 }
326
327 void
328 spiceterm_virtual_scroll(spiceTerm *vt, int lines)
329 {
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 }
354 }
355
356 spiceterm_refresh(vt);
357 }
358
359 void
360 spiceterm_respond_esc(spiceTerm *vt, const char *esc)
361 {
362 int len = strlen(esc);
363 int i;
364
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 }
370 } else {
371 fprintf(stderr, "input buffer oferflow\n");
372 return;
373 }
374 }
375
376 void
377 spiceterm_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;
388 }
389 }
390
391 static void
392 spiceterm_put_lf(spiceTerm *vt)
393 {
394 if (vt->cy + 1 == vt->region_bottom) {
395
396 if (vt->altbuf || vt->region_top != 0 || vt->region_bottom != vt->height) {
397 spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 1);
398 return;
399 }
400
401 if (vt->y_displ == vt->y_base) {
402 spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 0);
403 }
404
405 if (vt->y_displ == vt->y_base) {
406 if (++vt->y_displ == vt->total_height) {
407 vt->y_displ = 0;
408 }
409 }
410
411 if (++vt->y_base == vt->total_height) {
412 vt->y_base = 0;
413 }
414
415 if (vt->scroll_height < vt->total_height) {
416 vt->scroll_height++;
417 }
418
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 }
427
428 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
429
430 } else if (vt->cy < vt->height - 1) {
431 vt->cy += 1;
432 }
433 }
434
435 static void
436 spiceterm_csi_m(spiceTerm *vt)
437 {
438 int i;
439
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 }
531 }
532
533 static void
534 spiceterm_save_cursor(spiceTerm *vt)
535 {
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;
543 }
544
545 static void
546 spiceterm_restore_cursor(spiceTerm *vt)
547 {
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;
555 }
556
557 static void
558 spiceterm_set_alternate_buffer(spiceTerm *vt, int on_off)
559 {
560 int x, y;
561
562 vt->y_displ = vt->y_base;
563
564 if (on_off) {
565
566 if (vt->altbuf) return;
567
568 vt->altbuf = 1;
569
570 /* alternate buffer & cursor */
571
572 spiceterm_save_cursor(vt);
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 }
580
581 /* clear screen */
582 for (y = 0; y <= vt->height; y++) {
583 for (x = 0; x < vt->width; x++) {
584 // spiceterm_clear_xy(vt, x, y);
585 }
586 }
587
588 } else {
589
590 if (vt->altbuf == 0) return;
591
592 vt->altbuf = 0;
593
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 }
601
602 spiceterm_restore_cursor(vt);
603 }
604
605 spiceterm_refresh(vt);
606 }
607
608 static void
609 spiceterm_set_mode(spiceTerm *vt, int on_off)
610 {
611 int i;
612
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 */
617 case 1000: /* SET_VT200_MOUSE */
618 case 1002: /* xterm SET_BTN_EVENT_MOUSE */
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 */
625 case 9: /* X10 mouse reporting on/off */
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;
632 }
633 } else { /* ANSI modes set/reset */
634 //g_assert_not_reached();
635
636 /* fixme: implement me */
637 }
638 }
639 }
640
641 static void
642 spiceterm_gotoxy(spiceTerm *vt, int x, int y)
643 {
644 /* verify all boundaries */
645
646 if (x < 0) {
647 x = 0;
648 }
649
650 if (x >= vt->width) {
651 x = vt->width - 1;
652 }
653
654 vt->cx = x;
655
656 if (y < 0) {
657 y = 0;
658 }
659
660 if (y >= vt->height) {
661 y = vt->height - 1;
662 }
663
664 vt->cy = y;
665 }
666
667 static void
668 debug_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
687 enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
688 EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
689 ESpalette, ESidquery, ESosc1, ESosc2};
690
691 static void
692 spiceterm_putchar(spiceTerm *vt, gunichar2 ch)
693 {
694 int x, y, i, c;
695
696 if (debug && !vt->tty_state) {
697 DPRINTF(1, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d",
698 vt->tty_state, ch, ch, vt->cur_enc, vt->cx, vt->cy);
699 }
700
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':
715 spiceterm_save_cursor(vt);
716 break;
717 case '8':
718 spiceterm_restore_cursor(vt);
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)
729 spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, 1);
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:
741 DPRINTF(1, "got unhandled ESC%c %d", ch, ch);
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 }
753
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:
769 DPRINTF(1, "got unhandled OSC %c", ch);
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 {
779 DPRINTF(1, "got illegal OSC sequence");
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 {
789 DPRINTF(1, "OSC:%c:%s", vt->osc_cmd, vt->osc_textbuf);
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); ?
811
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 }
821
822 vt->esc_count = 0;
823 vt->esc_has_par = 0;
824 vt->tty_state = ESgetpars;
825
826 if (ch == '>') {
827 vt->tty_state = ESidquery;
828 break;
829 }
830
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;
844 } else {
845 if (vt->esc_has_par) {
846 vt->esc_count++;
847 }
848 vt->tty_state = ESgotpars;
849 }
850 case ESgotpars:
851
852 vt->tty_state = ESnormal;
853
854 char *qes = vt->esc_ques ? "?" : "";
855
856 if (debug) {
857 debug_print_escape_buffer(vt, __func__, "", qes, ch);
858 }
859
860 switch (ch) {
861 case 'h':
862 spiceterm_set_mode(vt, 1);
863 break;
864 case 'l':
865 spiceterm_set_mode(vt, 0);
866 break;
867 case 'm':
868 if (!vt->esc_count) {
869 vt->esc_count++; // default parameter 0
870 }
871 spiceterm_csi_m(vt);
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 */
922 spiceterm_gotoxy(vt, vt->esc_buf[0] - 1, vt->cy);
923 break;
924 case 'd':
925 /* move cursor to row */
926 spiceterm_gotoxy(vt, vt->cx , vt->esc_buf[0] - 1);
927 break;
928 case 'f':
929 case 'H':
930 /* move cursor to row, column */
931 spiceterm_gotoxy(vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1);
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 }
953 spiceterm_clear_xy(vt, x, y);
954 }
955 }
956 break;
957 case 2:
958 /* clear entire screen */
959 spiceterm_clear_screen(vt);
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++) {
968 spiceterm_clear_xy(vt, x, vt->cy);
969 }
970 break;
971 case 1:
972 /* clear from beginning of line */
973 for (x = 0; x <= vt->cx; x++) {
974 spiceterm_clear_xy(vt, x, vt->cy);
975 }
976 break;
977 case 2:
978 /* clear entire line */
979 for(x = 0; x < vt->width; x++) {
980 spiceterm_clear_xy(vt, x, vt->cy);
981 }
982 break;
983 }
984 break;
985 case 'L':
986 /* insert line */
987 c = vt->esc_buf[0];
988
989 if (c > vt->height - vt->cy)
990 c = vt->height - vt->cy;
991 else if (!c)
992 c = 1;
993
994 spiceterm_scroll_down(vt, vt->cy, vt->region_bottom, c);
995 break;
996 case 'M':
997 /* delete line */
998 c = vt->esc_buf[0];
999
1000 if (c > vt->height - vt->cy)
1001 c = vt->height - vt->cy;
1002 else if (!c)
1003 c = 1;
1004
1005 spiceterm_scroll_up(vt, vt->cy, vt->region_bottom, c, 1);
1006 break;
1007 case 'T':
1008 /* scroll down */
1009 c = vt->esc_buf[0];
1010 if (!c) c = 1;
1011 spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, c);
1012 break;
1013 case 'S':
1014 /* scroll up */
1015 c = vt->esc_buf[0];
1016 if (!c) c = 1;
1017 spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, c, 1);
1018 break;
1019 case 'P':
1020 /* delete c character */
1021 c = vt->esc_buf[0];
1022
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;
1033 spiceterm_update_xy(vt, x + c, vt->cy);
1034 src->ch = ' ';
1035 src->attrib = vt->default_attrib;
1036 spiceterm_update_xy(vt, x, vt->cy);
1037 }
1038 break;
1039 case 's':
1040 /* save cursor position */
1041 spiceterm_save_cursor(vt);
1042 break;
1043 case 'u':
1044 /* restore cursor position */
1045 spiceterm_restore_cursor(vt);
1046 break;
1047 case 'X':
1048 /* erase c characters */
1049 c = vt->esc_buf[0];
1050 if (!c) c = 1;
1051
1052 if (c > (vt->width - vt->cx)) c = vt->width - vt->cx;
1053
1054 for(i = 0; i < c; i++) {
1055 spiceterm_clear_xy(vt, vt->cx + i, vt->cy);
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;
1065
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;
1074 spiceterm_update_xy(vt, x, vt->cy);
1075 }
1076
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;
1091 DPRINTF(1, "set region %d %d", vt->region_top, vt->region_bottom);
1092 }
1093
1094 break;
1095 default:
1096 if (debug) {
1097 debug_print_escape_buffer(vt, __func__, " unhandled escape", qes, ch);
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;
1114
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') {
1139 DPRINTF(1, "ESC[>c Query term ID");
1140 spiceterm_respond_esc(vt, TERMIDCODE);
1141 }
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;
1172 spiceterm_put_lf(vt);
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 */
1180 spiceterm_put_lf(vt);
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;
1210 spiceterm_put_lf(vt);
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;
1217 spiceterm_update_xy(vt, vt->cx, vt->cy);
1218 vt->cx++;
1219 break;
1220 }
1221 break;
1222 }
1223 }
1224
1225 static int
1226 spiceterm_puts(spiceTerm *vt, const char *buf, int len)
1227 {
1228 gunichar2 tc;
1229
1230 spiceterm_show_cursor(vt, 0);
1231
1232 while (len) {
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 }
1280
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 }
1289
1290 spiceterm_putchar(vt, tc);
1291 }
1292
1293 spiceterm_show_cursor(vt, 1);
1294
1295 return len;
1296 }
1297
1298 void
1299 spiceterm_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
1312 static void
1313 mouse_report(spiceTerm *vt, int butt, int mrx, int mry)
1314 {
1315 char buf[8];
1316
1317 sprintf(buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
1318 (char)('!' + mry));
1319
1320 spiceterm_respond_esc(vt, buf);
1321
1322 spiceterm_update_watch_mask(vt, TRUE);
1323 }
1324
1325 static void
1326 spiceterm_respond_unichar2(spiceTerm *vt, gunichar2 uc)
1327 {
1328 if (vt->utf8) {
1329 gchar buf[10];
1330 gint len = g_unichar_to_utf8(uc, buf);
1331
1332 if (len > 0) {
1333 spiceterm_respond_data(vt, len, (uint8_t *)buf);
1334 }
1335 } else {
1336 uint8_t buf[1] = { (uint8_t)uc };
1337 spiceterm_respond_data(vt, 1, buf);
1338 }
1339 }
1340
1341 void
1342 spiceterm_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
1353 void
1354 spiceterm_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
1392 if (vdagent_owns_clipboard(vt)) {
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 {
1405 vdagent_request_clipboard(vt);
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
1477 vdagent_grab_clipboard(vt);
1478 }
1479 }
1480
1481 void
1482 init_spiceterm(spiceTerm *vt, uint32_t width, uint32_t height)
1483 {
1484 int i;
1485
1486 g_assert(vt != NULL);
1487 g_assert(vt->screen != NULL);
1488
1489 vt->width = width / 8;
1490 vt->height = height / 16;
1491
1492 vt->total_height = vt->height * 20;
1493 vt->scroll_height = 0;
1494 vt->y_base = 0;
1495 vt->y_displ = 0;
1496
1497 vt->region_top = 0;
1498 vt->region_bottom = vt->height;
1499
1500 vt->g0enc = LAT1_MAP;
1501 vt->g1enc = GRAF_MAP;
1502 vt->cur_enc = vt->g0enc;
1503 vt->charset = 0;
1504
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;
1513
1514 vt->cur_attrib = vt->default_attrib;
1515
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
1524 vt->cells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->total_height);
1525
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 }
1530
1531 if (vt->altcells) {
1532 g_free(vt->altcells);
1533 }
1534
1535 vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height);
1536 }
1537
1538 void
1539 spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height)
1540 {
1541 width = (width/8)*8;
1542 height = (height/16)*16;
1543
1544 if (vt->screen->width == width && vt->screen->height == height) {
1545 return;
1546 }
1547
1548 DPRINTF(0, "width=%u height=%u", width, height);
1549
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
1561 static gboolean
1562 master_error_callback(GIOChannel *channel, GIOCondition condition,
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
1574 static void
1575 master_watch(int master, int event, void *opaque)
1576 {
1577 spiceTerm *vt = (spiceTerm *)opaque;
1578 int c;
1579
1580 // fixme: if (!vt->mark_active) {
1581
1582 if (event == SPICE_WATCH_EVENT_READ) {
1583 char buffer[1024];
1584 while ((c = read(master, buffer, 1024)) == -1) {
1585 if (errno != EAGAIN) break;
1586 }
1587 if (c == -1) {
1588 perror("master pipe read error"); // fixme
1589 }
1590 spiceterm_puts (vt, buffer, c);
1591 } else {
1592 if (vt->ibuf_count > 0) {
1593 DPRINTF(1, "write input %x %d", vt->ibuf[0], vt->ibuf_count);
1594 if ((c = write (master, vt->ibuf, vt->ibuf_count)) >= 0) {
1595 if (c == vt->ibuf_count) {
1596 vt->ibuf_count = 0;
1597 } else if (c > 0) {
1598 // not all data written
1599 memmove(vt->ibuf, vt->ibuf + c, vt->ibuf_count - c);
1600 vt->ibuf_count -= c;
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);
1610 }
1611 }
1612 }
1613
1614 int
1615 main (int argc, char** argv)
1616 {
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 }
1635 }
1636
1637 if (0) print_usage(NULL); // fixme:
1638
1639 spiceTerm *vt = create_spiceterm (argc, argv, 744, 400, 10);
1640
1641 setlocale(LC_ALL, ""); // set from environment
1642
1643 char *ctype = setlocale (LC_CTYPE, NULL); // query LC_CTYPE
1644
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 }
1649
1650 dimensions.ws_col = vt->width;
1651 dimensions.ws_row = vt->height;
1652
1653 setenv("TERM", TERM, 1);
1654
1655 DPRINTF(1, "execute %s", command);
1656
1657 pid = forkpty (&master, ptyname, NULL, &dimensions);
1658 if(!pid) {
1659
1660 // install default signal handlers
1661 signal (SIGQUIT, SIG_DFL);
1662 signal (SIGTERM, SIG_DFL);
1663 signal (SIGINT, SIG_DFL);
1664
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);
1675 }
1676
1677 vt->pty = master;
1678
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);
1685
1686 vt->screen->mwatch = vt->screen->core->watch_add(
1687 master, SPICE_WATCH_EVENT_READ /* |SPICE_WATCH_EVENT_WRITE */,
1688 master_watch, vt);
1689
1690 basic_event_loop_mainloop();
1691
1692 kill (pid, 9);
1693 int status;
1694 waitpid(pid, &status, 0);
1695
1696 exit (0);
1697 }