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