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