.gitignore updates
[vncterm.git] / vncterm.c
index baaa379..ff24953 100644 (file)
--- a/vncterm.c
+++ b/vncterm.c
@@ -26,6 +26,9 @@
 #include <stdlib.h>
 #include <sys/types.h> 
 #include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <rfb/rfb.h>
@@ -39,7 +42,6 @@
 #include <locale.h>
 
 #include "vncterm.h"
-#include "glyphs.h"
 
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
 char *auth_path = "/";
 char *auth_perm = "Sys.Console";
 
+uint16_t screen_width = 744;
+uint16_t screen_height = 400;
+
 int use_x509 = 1;
 
+extern int wcwidth (wchar_t wc);
+unsigned char *fontdata;
+
+#define FONTFILE "/usr/share/vncterm/font.data"
+#define GLYPHLINES 16
+
 static char *
 urlencode(char *buf, const char *value)
 {
@@ -626,21 +637,16 @@ ucs2_to_utf8 (unicode c, char *out)
 
 static void
 rfb_draw_char (rfbScreenInfoPtr rfbScreen, int x, int y,
-              unsigned short c, rfbPixel col)
+              unicode c, rfbPixel col, short width)
 {
-  if (c > vt_font_size) {
-    rfbLog ("undefined font glyph %d\n", c);
-    return;
-  }
-
   int i,j;
-  unsigned char *data= vt_font_data + c*16;
+  unsigned char *data= fontdata + c*(GLYPHLINES*2);
   unsigned char d=*data;
   int rowstride=rfbScreen->paddedWidthInBytes;
   char *colour=(char*)&col;
 
-  for(j = 0; j < 16; j++) {
-    for(i = 0; i < 8; i++) {
+  for(j = 0; j < GLYPHLINES; j++) {
+    for(i = 0; i < 8*width; i++) {
       if ((i&7) == 0) {
        d=*data;
        data++;
@@ -653,13 +659,16 @@ rfb_draw_char (rfbScreenInfoPtr rfbScreen, int x, int y,
 }
 
 static void
-draw_char_at (vncTerm *vt, int x, int y, unicode ch, TextAttributes attrib)
+draw_char_at (vncTerm *vt, int x, int y, unicode ch, TextAttributes attrib, short width, unicode combiningglyph)
 {
   if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
 
+  // non printable character
+  if (width < 1) return;
+
   int rx = x*8;
   int ry = y*16;
-  int rxe = x*8+8;
+  int rxe = x*8+8*width;
   int rye = y*16+16;
 
   int fg, bg;
@@ -672,8 +681,6 @@ draw_char_at (vncTerm *vt, int x, int y, unicode ch, TextAttributes attrib)
     fg = attrib.fgcol;
   }
 
-  int ec = vt_fontmap[ch];
-
   rfbFillRect (vt->screen, rx, ry, rxe, rye, bg);
 
   if (attrib.bold) {
@@ -682,7 +689,11 @@ draw_char_at (vncTerm *vt, int x, int y, unicode ch, TextAttributes attrib)
 
   // unsuported attributes = (attrib.blink || attrib.unvisible)
 
-  rfb_draw_char (vt->screen, rx, ry, ec, fg);
+  rfb_draw_char (vt->screen, rx, ry, ch, fg, width);
+
+  if (combiningglyph) {
+      rfb_draw_char (vt->screen, rx, ry, combiningglyph, fg, 1);
+  }
 
   if (attrib.uline) {
     rfbDrawLine (vt->screen, rx, ry + 14, rxe, ry + 14, fg);
@@ -704,7 +715,7 @@ vncterm_update_xy (vncTerm *vt, int x, int y)
   }
   if (y2 < vt->height) {
     TextCell *c = &vt->cells[y1 * vt->width + x];
-    draw_char_at (vt, x, y2, c->ch, c->attrib);
+    draw_char_at (vt, x, y2, c->ch, c->attrib, c->width, c->combiningglyph);
   }
 }
 
@@ -724,8 +735,10 @@ vncterm_clear_xy (vncTerm *vt, int x, int y)
     c->attrib = vt->default_attrib;
     c->attrib.fgcol = vt->cur_attrib.fgcol;
     c->attrib.bgcol = vt->cur_attrib.bgcol;
+    c->width = 1;
+    c->combiningglyph = 0;
 
-    draw_char_at (vt, x, y, c->ch, c->attrib);
+    draw_char_at (vt, x, y, c->ch, c->attrib, c->width, c->combiningglyph);
   }
 }
 
@@ -750,9 +763,9 @@ vncterm_show_cursor (vncTerm *vt, int show)
     if (show) {
       TextAttributes attrib = vt->default_attrib;
       attrib.invers = !(attrib.invers); /* invert fg and bg */
-      draw_char_at (vt, x, y, c->ch, attrib);
+      draw_char_at (vt, x, y, c->ch, attrib, c->width, c->combiningglyph);
     } else {
-      draw_char_at (vt, x, y, c->ch, c->attrib);
+      draw_char_at (vt, x, y, c->ch, c->attrib, c->width, c->combiningglyph);
     }
   }
 }
@@ -768,8 +781,8 @@ vncterm_refresh (vncTerm *vt)
   for(y = 0; y < vt->height; y++) {
     TextCell *c = vt->cells + y1 * vt->width;
     for(x = 0; x < vt->width; x++) {
-      draw_char_at (vt, x, y, c->ch, c->attrib);
-      c++;
+      draw_char_at (vt, x, y, c->ch, c->attrib, c->width, c->combiningglyph);
+      c += c->width;
     }
     if (++y1 == vt->total_height)
       y1 = 0;
@@ -818,6 +831,8 @@ vncterm_scroll_down (vncTerm *vt, int top, int bottom, int lines)
     for(j = 0; j < vt->width; j++) {
       c->attrib = vt->default_attrib;
       c->ch = ' ';
+      c->width = 1;
+      c->combiningglyph = 0;
       c++;
     }
   }
@@ -867,6 +882,8 @@ vncterm_scroll_up (vncTerm *vt, int top, int bottom, int lines, int moveattr)
     for(j = 0; j < vt->width; j++) {
       c->attrib = vt->default_attrib;
       c->ch = ' ';
+      c->width = 1;
+      c->combiningglyph = 0;
       c++;
     }
   }
@@ -951,6 +968,8 @@ vncterm_put_lf (vncTerm *vt)
     int x;
     for (x = 0; x < vt->width; x++) {
       c->ch = ' ';
+      c->width = 1;
+      c->combiningglyph = 0;
       c->attrib = vt->default_attrib;
       c++;
     }
@@ -1173,9 +1192,7 @@ vncterm_gotoxy (vncTerm *vt, int x, int y)
 
   if (x < 0) {
     x = 0;
-  }
-
-  if (x >= vt->width) {
+  } else if (x >= vt->width) {
     x = vt->width - 1;
   }
 
@@ -1183,9 +1200,7 @@ vncterm_gotoxy (vncTerm *vt, int x, int y)
 
   if (y < 0) {
     y = 0;
-  }
-
-  if (y >= vt->height) {
+  } else if (y >= vt->height) {
     y = vt->height - 1;
   }
 
@@ -1405,10 +1420,7 @@ vncterm_putchar (vncTerm *vt, unicode ch)
       if (vt->esc_buf[0] == 0) {
        vt->esc_buf[0] = 1;
       }
-      vt->cy -= vt->esc_buf[0];
-      if (vt->cy < 0) {
-       vt->cy = 0;
-      }
+      vncterm_gotoxy (vt, vt->cx, vt->cy - vt->esc_buf[0]);
       break;
     case 'B':
     case 'e':
@@ -1416,10 +1428,7 @@ vncterm_putchar (vncTerm *vt, unicode ch)
       if (vt->esc_buf[0] == 0) {
        vt->esc_buf[0] = 1;
       }
-      vt->cy += vt->esc_buf[0];
-      if (vt->cy >= vt->height) {
-       vt->cy = vt->height - 1;
-      }
+      vncterm_gotoxy (vt, vt->cx, vt->cy + vt->esc_buf[0]);
       break;
     case 'C':
     case 'a':
@@ -1427,20 +1436,14 @@ vncterm_putchar (vncTerm *vt, unicode ch)
       if (vt->esc_buf[0] == 0) {
        vt->esc_buf[0] = 1;
       }
-      vt->cx += vt->esc_buf[0];
-      if (vt->cx >= vt->width) {
-       vt->cx = vt->width - 1;
-      }
+      vncterm_gotoxy (vt, vt->cx + vt->esc_buf[0], vt->cy);
       break;
     case 'D':
       /* move cursor left */
       if (vt->esc_buf[0] == 0) {
        vt->esc_buf[0] = 1;
       }
-      vt->cx -= vt->esc_buf[0];
-      if (vt->cx < 0) {
-       vt->cx = 0;
-      }
+      vncterm_gotoxy (vt, vt->cx - vt->esc_buf[0], vt->cy);
       break;
     case 'G':
     case '`':
@@ -1562,6 +1565,8 @@ vncterm_putchar (vncTerm *vt, unicode ch)
        *dst = *src;
        vncterm_update_xy (vt, x + c, vt->cy);
        src->ch = ' ';
+       src->width = 1;
+       src->combiningglyph = 0;
        src->attrib = vt->default_attrib;
        vncterm_update_xy (vt, x, vt->cy);
       }
@@ -1600,6 +1605,8 @@ vncterm_putchar (vncTerm *vt, unicode ch)
        *dst = *src;
        vncterm_update_xy (vt, x + c, vt->cy);
        src->ch = ' ';
+       src->width = 1;
+       src->combiningglyph = 0;
        src->attrib = vt->cur_attrib;
        vncterm_update_xy (vt, x, vt->cy);
       }
@@ -1755,11 +1762,25 @@ vncterm_putchar (vncTerm *vt, unicode ch)
       }
 
       int y1 = (vt->y_base + vt->cy) % vt->total_height;
-      TextCell *c = &vt->cells[y1*vt->width + vt->cx];
-      c->attrib = vt->cur_attrib;
-      c->ch = ch;
-      vncterm_update_xy (vt, vt->cx, vt->cy);
-      vt->cx++;
+      int width = wcwidth(ch);
+      if (width > 0) {
+         // normal/wide character
+         TextCell *c = &vt->cells[y1*vt->width + vt->cx];
+         c->attrib = vt->cur_attrib;
+         c->ch = ch;
+         c->width = width;
+         c->combiningglyph = 0;
+         vncterm_update_xy (vt, vt->cx, vt->cy);
+         vt->cx += width;
+      } else if (width == 0) {
+         // combiningglyph
+         TextCell *c = &vt->cells[y1*vt->width + vt->cx - 1];
+         c->attrib = vt->cur_attrib;
+         c->combiningglyph = ch;
+         vncterm_update_xy (vt, vt->cx - 1, vt->cy);
+      } else {
+         // non printable character, so we do not save them
+      }
       break;
     }
     break;
@@ -1790,7 +1811,11 @@ vncterm_puts (vncTerm *vt, const char *buf, int len)
            vt->utf_char = (vt->utf_char << 6) | (c & 0x3f);
            vt->utf_count--;
            if (vt->utf_count == 0) {
-             tc = vt->utf_char;
+               if (vt->utf_char <= USHRT_MAX) {
+                 tc = vt->utf_char;
+               } else {
+                 tc = 0;
+               }
            } else {
              continue;
            }
@@ -2176,6 +2201,63 @@ new_client (rfbClientPtr client)
 
 static char *vncticket = NULL;
 
+static void
+MakeRichCursor(rfbScreenInfoPtr rfbScreen)
+{
+  int w = 16,
+      h = 16;
+  rfbCursorPtr c = rfbScreen->cursor;
+  char bitmap[] =
+      "                "
+      " x              "
+      " xx             "
+      " xxx            "
+      " xxxx           "
+      " xxxxx          "
+      " xxxxxx         "
+      " xxxxxxx        "
+      " xxxxxxxx       "
+      " xxxxxxxxx      "
+      " xxxxxxxxxx     "
+      " xxxx           "
+      " xxx            "
+      " xx             "
+      " x              "
+      "                ";
+  char edge[] =
+      "                "
+      " x              "
+      " xx             "
+      " x x            "
+      " x  x           "
+      " x   x          "
+      " x    x         "
+      " x     x        "
+      " x      x       "
+      " x       x      "
+      " x   xxxxxx     "
+      " x  x           "
+      " x x            "
+      " xx             "
+      " x              "
+      "                ";
+
+  c = rfbScreen->cursor = rfbMakeXCursor(w,h,bitmap,bitmap);
+  c->richSource = (unsigned char*)calloc(w*h, 1);
+  c->cleanupRichSource = TRUE;
+
+  for(int j=0;j<h;j++) {
+    for(int i=0;i<w;i++) {
+      unsigned int pos = j*w+i;
+      if (edge[pos] == 'x') {
+       c->richSource[pos] = 15; // white
+      } else {
+       c->richSource[pos] = 0;  // black
+      }
+    }
+  }
+}
+
 vncTerm *
 create_vncterm (int argc, char** argv, int maxx, int maxy)
 {
@@ -2183,6 +2265,7 @@ create_vncterm (int argc, char** argv, int maxx, int maxy)
 
   rfbScreenInfoPtr screen = rfbGetScreen (&argc, argv, maxx, maxy, 8, 1, 1);
   screen->frameBuffer=(char*)calloc(maxx*maxy, 1);
+  MakeRichCursor(screen);
 
   char **passwds = calloc(sizeof(char**), 2);
 
@@ -2275,6 +2358,8 @@ main (int argc, char** argv)
   int i;
   char **cmdargv = NULL;
   char *command = "/bin/bash"; // execute normal shell as default
+  int fontfd;
+  struct stat sb;
   int pid;
   int master;
   char ptyname[1024];
@@ -2282,6 +2367,8 @@ main (int argc, char** argv)
   struct timeval tv, tv1;
   time_t elapsed, cur_time;
   struct winsize dimensions;
+  unsigned long width = 0;
+  unsigned long height = 0;
 
   if (gnutls_global_init () < 0) {
          fprintf(stderr, "gnutls_global_init failed\n");
@@ -2321,6 +2408,22 @@ main (int argc, char** argv)
       CHECK_ARGC (argc, argv, i);
       auth_perm = argv[i+1];
       rfbPurgeArguments(&argc, &i, 2, argv); i--;
+    } else if (!strcmp (argv[i], "-width")) {
+      CHECK_ARGC (argc, argv, i);
+      errno = 0;
+      width = strtoul(argv[i+1], NULL, 10);
+      if (errno == 0 && width >= 16 && width < 0xFFFF) {
+       screen_width = width;
+      }
+      rfbPurgeArguments(&argc, &i, 2, argv); i--;
+    } else if (!strcmp (argv[i], "-height")) {
+      CHECK_ARGC (argc, argv, i);
+      errno = 0;
+      height = strtoul(argv[i+1], NULL, 10);
+      if (errno == 0 && height >= 32 && height < 0xFFFF) {
+       screen_height = height;
+      }
+      rfbPurgeArguments(&argc, &i, 2, argv); i--;
     } else if (!strcmp (argv[i], "-notls")) {
         rfbPurgeArguments(&argc, &i, 1, argv); i--;
         if ((vncticket = getenv("PVE_VNC_TICKET")) == NULL) {
@@ -2340,7 +2443,24 @@ main (int argc, char** argv)
   rfbLogEnable (0);
 #endif
 
-  vncTerm *vt = create_vncterm (argc, argv, 745, 400);
+  // mmap font file
+  fontfd = open(FONTFILE, O_RDONLY);
+  if (fontfd == -1) {
+    perror("Error opening Fontfile 'FONTFILE'");
+    exit (-1);
+  }
+  if (fstat(fontfd, &sb) == -1) {
+    perror("Stat on 'FONTFILE' failed");
+    exit (-1);
+  }
+  fontdata = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fontfd, 0);
+  if (fontdata == MAP_FAILED) {
+    perror("Could not mmap 'FONTFILE'");
+    exit (-1);
+  }
+
+  close(fontfd);
+  vncTerm *vt = create_vncterm (argc, argv, screen_width, screen_height);
 
   setlocale(LC_ALL, ""); // set from environment
 
@@ -2440,9 +2560,12 @@ main (int argc, char** argv)
     }
   }
 
+  rfbScreenCleanup(vt->screen);
+
   kill (pid, 9);
   int status;
   waitpid(pid, &status, 0);
 
+  munmap(fontdata, sb.st_size);
   exit (0);
 }