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