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