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