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