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