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