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