Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

st2205term.c ver 2

By: a guest on Apr 26th, 2012  |  syntax: C  |  size: 29.02 KB  |  views: 41  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /***
  2.     This program is free software: you can redistribute it and/or modify
  3.     it under the terms of the GNU General Public License as published by
  4.     the Free Software Foundation, either version 3 of the License, or
  5.     (at your option) any later version.
  6.  
  7.     This program is distributed in the hope that it will be useful,
  8.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.     GNU General Public License for more details.
  11.  
  12.     You should have received a copy of the GNU General Public License
  13.     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  14. ***/
  15.  
  16. // To compile, choose one of the following depending on what display libraries you have...
  17. // libst2205:            gcc -Wall -g -O2 -DHAVE_ST2205 -o st2205term st2205term.c -lrote -lst2205
  18. // libdlo:               gcc -Wall -g -O2 -DHAVE_DLO -o st2205term st2205term.c -lrote -ldlo
  19. // libst2205 and libdlo: gcc -Wall -g -O2 -DHAVE_ST2205 -DHAVE_DLO -o st2205term st2205term.c -lrote -lst2205 -ldlo
  20.  
  21. #define _GNU_SOURCE
  22. #include <stdio.h>
  23. #include <limits.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <unistd.h>
  27. #include <time.h>
  28. #include <dirent.h>
  29.  
  30. #include <errno.h>
  31.  
  32. #include <fcntl.h>
  33. #include <sys/ioctl.h>
  34. #include <linux/input.h>
  35. #include <rote/rote.h>
  36. #include <signal.h>
  37. #include <fcntl.h>
  38. #include <sys/stat.h>
  39. #include <sys/time.h>
  40.  
  41. #ifdef HAVE_ST2205
  42. #include <st2205.h>
  43. static st2205_handle *picframe=NULL; // libst2205 instance handle
  44. #endif
  45. char *st2205dev=NULL; // name of picframe device (commandline)
  46.  
  47. #ifdef HAVE_DLO
  48. #include "libdlo.h"
  49. static dlo_dev_t uid=0;
  50. static int minx,maxx,miny,maxy;
  51. static unsigned char colormap[8]={0,0,0};
  52. static int dlo_broken=0;
  53. static int *source_row=NULL;
  54. static unsigned char *bad_row=NULL;
  55. static unsigned int *lasthash=NULL,*hash=NULL; // 32bit hash of each terminal row
  56. #endif
  57. #ifdef HAVE_DPF
  58. #include "dpf.h"
  59. static DPFHANDLE picframe;
  60. static unsigned char colormap[8];
  61. #endif
  62.  
  63. static int sw=0,sh=0; // screen width, height
  64. static unsigned char *pixels=NULL; // pixel buffer that is copied to device
  65. typedef struct _font_t {
  66.     int w,h;
  67.     char data[1]; // stretchy
  68. } font_t;
  69. static font_t *font=NULL;
  70.  
  71. static char *command="/bin/bash --login"; // command to run on main terminal
  72. static char *command2=NULL; // command to run as screen saver
  73.  
  74. static RoteTerm *rt1=NULL; // main terminal (command)
  75. static RoteTerm *rt2=NULL; // terminal for screensaver (command2)
  76. static int tw=0,th=0; // terminal width, height in chars
  77. static unsigned char *lastbuf=NULL,*lastattr=NULL; // last terminal image
  78. static int last_cy=0,last_cx=0; // last cursor position
  79.  
  80. // st2205term supports 8 colors (same as Linux console):
  81. static unsigned char rgb_colors[8][3]=
  82. {
  83.     { 0,0,0,     }, // black
  84.     { 255,0,0,   }, // red
  85.     { 0,255,0,   }, // green
  86.     { 255,255,0, }, // yellow
  87.     { 0,0,255,   }, // blue
  88.     { 255,0,255, }, // magenta
  89.     { 0,255,255, }, // cyan
  90.     { 255,255,255}  // white
  91. };
  92.  
  93. // array that maps evdev keycode => linux key
  94. typedef struct _keymap {
  95.     char keyname[16];     // name of key
  96.     int  ch;              // TERM=linux key code
  97.     int  shift_ch;        // TERM=linux key code while SHIFT is pressed
  98.     int  ctrl_ch;         // TERM=linux key code while CONTROL is pressed
  99.     int  alt_ch;          // TERM=linux key code while ALT is pressed
  100. } keymap;
  101.  
  102. #define MAX_EVKEYS 256
  103. static keymap kmap[MAX_EVKEYS]={{"",0},{"",0},{"",0}};
  104. static int shift_active=0,ctrl_active=0,alt_active=0;
  105.  
  106. // type of output device selected on cmdline
  107. static int use_st2205=0,use_dlo=0,use_dpf=0;
  108.  
  109. // blinking cursor?
  110. static int blinking_cursor=0,blink_toggle=1;
  111. static struct timeval blink_last={0,0};
  112.  
  113. static int kmap2int(char *str)
  114. {
  115.     int n;
  116.     if (*str=='\'' || *str=='"') return str[1];
  117.     if (*str=='0' && str[1]=='x')
  118.         if (sscanf(str+2,"%x",&n)==1)
  119.             return n;
  120.     if (*str>='0' && *str<='9')
  121.        return atoi(str);
  122.     if (*str) fprintf(stderr,"Warning: kmap2int(%s) - number conversion failed\n",str);
  123.     return 0;
  124. }
  125. static void load_kmap(char *file)
  126. {
  127.     fprintf(stderr,"load_kmap(%s) - loading key defintions\n",file);
  128.     FILE *f=fopen(file,"r");
  129.     if (!f) { perror("fopen"); fprintf(stderr,"ERROR: could not open file : %s\n",file); exit(1); }
  130.     char line[256];
  131.     int count=0;
  132.     char keyname[16],code[8],base[8],shift[8],ctrl[8],alt[8];
  133.     while (fgets(line,sizeof(line)-1,f))
  134.     {
  135.         if (sscanf(line,"%15s %7s %7s %7s %7s %7s",keyname,code,base,shift,ctrl,alt)==6)
  136.         {
  137.             if (keyname[0]=='#') continue;
  138.             int k=kmap2int(code);
  139.             if (k>=MAX_EVKEYS)
  140.             { fprintf(stderr,"load_kmap(%s): Warning ignoring keycode out-of-range: %s",file,line); continue; }
  141.  
  142.             strcpy(kmap[k].keyname,keyname);
  143.             kmap[k].ch=kmap2int(base);
  144.             kmap[k].shift_ch=kmap2int(shift);
  145.             kmap[k].ctrl_ch=kmap2int(ctrl);
  146.             kmap[k].alt_ch=kmap2int(alt);
  147.             count++;
  148.         }
  149.         else if (sscanf(line,"%1s",keyname)==1) // ignore blank lines
  150.             fprintf(stderr,"load_kmap(%s): ERROR Syntax: %s",file,line);
  151.     }
  152.     fclose(f);
  153.     fprintf(stderr,"load_kmap(%s) - %d key defintions loaded\n",file,count);
  154. }
  155. // return codes:
  156. //  0 => no mapping
  157. // -1 => special key (SHIFT,CTRL,ALT)
  158. // >0 => Linux key code
  159. static int evkey2linuxkey(int code,int pressed)
  160. {
  161.     if (code>=MAX_EVKEYS) return 0;
  162.     if (kmap[code].keyname[0]==0) return 0;
  163.  
  164.     if (strcmp(kmap[code].keyname,"SHIFT_LEFT")==0 || strcmp(kmap[code].keyname,"SHIFT_RIGHT")==0)
  165.     { shift_active=pressed; return -1; }
  166.     if (strcmp(kmap[code].keyname,"ALT_LEFT")==0 || strcmp(kmap[code].keyname,"ALT_RIGHT")==0)
  167.     { alt_active=pressed; return -1; }
  168.     if (strcmp(kmap[code].keyname,"CTRL")==0)
  169.     { ctrl_active=pressed; return -1; }
  170.  
  171.     int linuxkey=0;
  172.     if (pressed)
  173.     {
  174.         linuxkey=kmap[code].ch;
  175.         if (alt_active) linuxkey=kmap[code].alt_ch;
  176.         if (ctrl_active) linuxkey=kmap[code].ctrl_ch;
  177.         if (shift_active) linuxkey=kmap[code].shift_ch;
  178.     }
  179.     return linuxkey;
  180. }
  181.  
  182. static font_t *load_font(char *file,int num)
  183. {
  184.     fprintf(stderr,"load_font(%s,%d) - loading font defintion\n",file,num);
  185.     FILE *fd=fopen(file,"r");
  186.     if (!fd) { perror("fopen"); fprintf(stderr,"ERROR: could not open file : %s\n",file); exit(1); }
  187.     int font_size[4][2];
  188.     if (fscanf(fd,"%d %d %d %d %d %d %d %d",
  189.                &font_size[0][0],&font_size[0][1],&font_size[1][0],&font_size[1][1],
  190.                &font_size[2][0],&font_size[2][1],&font_size[3][0],&font_size[3][1])!=8)
  191.     { fprintf(stderr,"ERROR: could read font sizes from file : %s\n",file); exit(1); }
  192.      
  193.     int f,sz;
  194.     for (f=0;f<num;f++)
  195.     {
  196.         sz=95*(font_size[f][0]+1)*font_size[f][1];
  197.         if (fseek(fd,sz,SEEK_CUR)<0)
  198.         { perror("fseek "); fprintf(stderr,"ERROR: could read font %d (sz=%d) from file : %s\n",f,sz,file); exit(1); }
  199.     }
  200.  
  201.     sz=95*(font_size[f][0]+1)*font_size[f][1];
  202.     font_t *d=(font_t *)malloc(sizeof(font_t)+sz);
  203.     d->w=font_size[f][0];
  204.     d->h=font_size[f][1];
  205.    
  206.     if (fread(d->data,1,sz,fd)!=sz)
  207.     { fprintf(stderr,"ERROR: could read font %d (sz=%d) from file : %s\n",f,sz,file); exit(1); }
  208.     fclose(fd);
  209.  
  210.     return d;
  211. }
  212.  
  213. // try to open keyboard device
  214. static int check_for_keyboard(char *kbdev)
  215. {
  216.     char device[PATH_MAX],path[PATH_MAX];
  217.  
  218.     char *dev=kbdev;
  219.     if (dev==NULL)
  220.     {
  221.         DIR *d=opendir("/dev/input/by-id/");
  222.         if (!d) return -997;
  223.        
  224.         struct dirent *e;
  225.         while ((e=readdir(d))!=NULL)
  226.         {
  227.             if (strstr(e->d_name,"Keyboard-event-kbd")!=NULL)
  228.             { sprintf(path,"/dev/input/by-id/%s",e->d_name); break; }
  229.         }
  230.         closedir(d);
  231.         if (e==NULL) return -998;
  232.         dev=path;
  233.     }
  234.  
  235.     if (realpath(dev,device)==NULL)
  236.     { fprintf(stderr,"WARN: realpath failed to resolve device %s\n",kbdev); return -999; }
  237.        
  238.     int fd=open(device,O_RDONLY);
  239.     if (fd>=0)
  240.     {
  241.         fcntl(fd,F_SETFL,O_NONBLOCK);
  242.         char name[256]= "Unknown";
  243.         if (ioctl(fd,EVIOCGNAME(sizeof(name)),name)>=0)
  244.             fprintf(stderr,"Opened device %s (%s) => %s\n",kbdev!=NULL?kbdev:path,device,name);
  245.     }
  246.     return fd;
  247. }
  248.  
  249. // draw a character on the pixel buffer
  250. static void draw_char(unsigned char *pixels,int x,int y,int ch,int bg,int fg,font_t *f,int sh,int sw)
  251. {
  252.     x*=f->w; y*=f->h; // char offsets => pixel offsets
  253.  
  254. #ifdef HAVE_ST2205
  255.     if (use_st2205)
  256.     {
  257.         char *d=f->data;
  258.         if (ch>32 && ch<127) d+=(f->w+1)*f->h*(ch-32);
  259.         unsigned char *rgb,*rgb_fg=rgb_colors[fg],*rgb_bg=rgb_colors[bg];
  260.         int m,n;
  261.         for (m=0;m<f->h;m++) {
  262.             unsigned char *p=pixels+(y+m)*(sw+sw+sw)+x+x+x;
  263.             for (n=0;n<f->w;n++) {
  264.                 rgb=(*d++=='#')?rgb_fg:rgb_bg;
  265.                 *p++=*rgb++;
  266.                 *p++=*rgb++;
  267.                 *p++=*rgb++;
  268.             }
  269.             d++; // skip linefeed
  270.         }
  271.     }
  272. #endif
  273. #ifdef HAVE_DPF
  274.     if (use_dpf)
  275.     {
  276.         char *d=f->data;
  277.         if (ch>32 && ch<127) d+=(f->w+1)*f->h*(ch-32);
  278.         unsigned char rgb,rgb_fg=colormap[fg],rgb_bg=colormap[bg];
  279.         int m,n;
  280.         for (m=0;m<f->h;m++) { //Each font character if a rectangular array of pixels.
  281. //m is the y-coordinate within the pixel array which ranges from 0..font height (f->h-1)
  282.             unsigned char *p=pixels+(y+m)*(sw+sw)+x+x;
  283.                
  284.             for (n=0;n<f->w;n++) {
  285. // n is the x-coordinate within the pixel array which ranges from 0..font width (f->w-1)
  286.                 rgb=(*d++=='#')?rgb_fg:rgb_bg;
  287.                 *p++=rgb++;
  288.                 *p++=rgb++;
  289.             }
  290.             d++; // skip linefeed
  291.         }
  292.     }  
  293. #endif
  294. #ifdef HAVE_DLO
  295.     if (use_dlo)
  296.     {
  297.         if (x<minx) minx=x;
  298.         if (x+f->w>maxx) maxx=x+f->w;
  299.         if (y<miny) miny=y;
  300.         if (y+f->h>maxy) maxy=y+f->h;
  301.  
  302.         char *d=f->data;
  303.         if (ch>32 && ch<127) d+=(f->w+1)*f->h*(ch-32);
  304.         unsigned char dlo_fg=colormap[fg],dlo_bg=colormap[bg];
  305.         int m;
  306.         for (m=0;m<f->h;m++)
  307.         {
  308.             unsigned char *p=pixels+(m+y)*sw+x,*pe=p+f->w;
  309.             while (p<pe)
  310.                 *p++=*d++=='#'?dlo_fg:dlo_bg;
  311.             d++; // skip linefeed
  312.         }
  313.     }
  314. #endif
  315. }
  316.  
  317. static void create_terminal()
  318. {
  319.     tw=sw/font->w; th=sh/font->h;
  320.     fprintf(stderr,"create_terminal %dx%d pixels => %dx%d chars\n",sw,sh,tw,th);
  321.     rt1=rote_vt_create(th,tw);
  322.     pid_t pid=rote_vt_forkpty(rt1,command);
  323.     fprintf(stderr,"rote_vt_forkpty: command='%s' PID=%d\n",command,pid);
  324.     lastbuf=(unsigned char *)calloc(th*tw,1);
  325.     lastattr=(unsigned char *)calloc(th*tw,1);
  326.     last_cy=0; last_cx=0;
  327.  
  328.     if (command2!=NULL)
  329.     {
  330.         rt2=rote_vt_create(th,tw);
  331.         pid_t pid=rote_vt_forkpty(rt2,command2);
  332.         fprintf(stderr,"rote_vt_forkpty: screensaver command='%s' PID=%d\n",command2,pid);
  333.     }
  334.  
  335.     #ifdef HAVE_DLO
  336.         #define DLO_MAP(R,G,B) ((((R)/32)<<5)+(((G)/64)<<3)+((B)/32))
  337.         int i;
  338.         for (i=0;i<8;i++)
  339.             colormap[i]=DLO_MAP(rgb_colors[i][0],rgb_colors[i][1],rgb_colors[i][2]);
  340.         lasthash=(unsigned int *)calloc(th,sizeof(int));
  341.         hash=(unsigned int *)calloc(th,sizeof(int));
  342.         source_row=(int*)calloc(th,sizeof(int));
  343.         bad_row=(unsigned char*)calloc(th,sizeof(unsigned char));
  344.     #endif
  345.     #ifdef HAVE_DPF
  346.         int i;
  347.         for (i=0;i<8;i++)
  348.             colormap[i]=RGB565_S(rgb_colors[i][0],rgb_colors[i][1],rgb_colors[i][2]);
  349.     #endif
  350.  
  351. }
  352. static void destroy_terminal()
  353. {
  354.     fprintf(stderr,"Destroying terminal - output device unplugged\n");
  355.     rote_vt_destroy(rt1); rt1=NULL;
  356.     tw=0; th=0;
  357.     free(lastbuf); lastbuf=NULL;
  358.     free(lastattr); lastattr=NULL;
  359.     last_cy=0; last_cx=0;
  360.  
  361.     if (rt2!=NULL) { rote_vt_destroy(rt2); rt2=NULL; }
  362.  
  363. #ifdef HAVE_DLO
  364.     free(lasthash); lasthash=NULL;
  365.     free(hash); hash=NULL;
  366.     free(source_row); source_row=NULL;
  367.     free(bad_row); bad_row=NULL;
  368. #endif
  369. }
  370.  
  371. static void check_for_output_device()
  372. {
  373.     if (use_dlo)
  374.     {
  375. #ifdef HAVE_DLO
  376.         if (uid)
  377.         { // we think we're connected but are we really?
  378.             if (dlo_broken)
  379.             {
  380.                 destroy_terminal();
  381.                 dlo_release_device(uid);
  382. //                dlo_final_t fin_flags = { 0 };
  383. //                dlo_final(fin_flags);
  384.                 uid=0;
  385.             }
  386.         }
  387.         else
  388.         { // we're disconnected - see if we can connect
  389.             static int init=0;
  390.             dlo_init_t ini_flags={ 0 };
  391.             dlo_claim_t cnf_flags={ 0 };
  392.             if (!init) // only dlo_init() once - multiple init+final is buggy
  393.             {
  394.                 if (dlo_init(ini_flags)!=dlo_ok)
  395.                 { fprintf(stderr,"ERROR: dlo_init() failed!\n"); exit(1); }
  396.                 init=1;
  397.             }
  398.             uid=dlo_claim_first_device(cnf_flags,0);
  399.             if (uid)
  400.             {
  401.                 dlo_mode_t *info=dlo_get_mode(uid);
  402.                 sw=info->view.width;
  403.                 sh=info->view.height;
  404. /*     // trying to set device to 8bpp seems to have no effect - so dont bother
  405.         dlo_mode_t desc;
  406.         desc.view.base=info->view.base;
  407.         desc.view.width=info->view.width;
  408.         desc.view.height=info->view.height;
  409.         desc.view.bpp=8;
  410.         desc.refresh=0;
  411.         if (dlo_set_mode(uid,&desc)!=dlo_ok)
  412.             fprintf(stderr,"Warning: could not det displaylink mode to 8bpp\n");
  413.         info=dlo_get_mode(uid);
  414.         sw=info->view.width;
  415.         sh=info->view.height; */
  416.                 fprintf(stderr,"Displaylink device: %d x %d, %d bpp color\n",sw,sh,info->view.bpp);
  417.                 pixels=(unsigned char*)calloc(1,sw*sh); // 8bpp RRRGGBBB bits
  418.                 dlo_fill_rect(uid,NULL,NULL,DLO_RGB(0,0,0)); // clear screen black
  419.                 dlo_broken=0;
  420.                 create_terminal();
  421.             }
  422. //            else { dlo_final_t fin_flags = { 0 }; dlo_final(fin_flags); }
  423.         }
  424. #else
  425.         fprintf(stderr,"ERROR: displaylink support is not compiled into binary!\n"); exit(1);
  426. #endif
  427.     }
  428.     if (use_dpf)
  429.     {
  430. #ifdef HAVE_DPF
  431.         static char device[PATH_MAX];
  432.         if (picframe==NULL)
  433.         {
  434.             char *dev=NULL;
  435.             if (dev==NULL)
  436.             {
  437.                 static char path[PATH_MAX];
  438.                 dev="usb0";
  439.             }
  440.             strcpy(device,dev);
  441.             int r=dpf_open(device,&picframe);
  442.                 if (picframe)
  443.                 {
  444.                     sw=picframe->width;
  445.                     sh=picframe->height;
  446.                     pixels=(unsigned char*)malloc(sw*sh*2);
  447.                     create_terminal();
  448.                     fprintf(stderr,"INFO: successfully connected to picframe\n");
  449.                 }
  450.                 else fprintf(stderr,"WARN: failed to connect to picframe\n");
  451.             //}
  452.         }
  453. #else
  454.         fprintf(stderr,"ERROR: dpf support is not compiled into binary!\n"); exit(1);
  455. #endif
  456.     }
  457.     if (use_st2205)
  458.     {
  459. #ifdef HAVE_ST2205
  460.         static char device[PATH_MAX];
  461.         if (picframe)
  462.         { // we think we're connected but are we really?
  463.             struct stat s; // stat device to see if it is still there
  464.             int rc=stat(device,&s);
  465.             if (rc<0)
  466.             {
  467.                 destroy_terminal();
  468.                 st2205_close(picframe);
  469.                 free(pixels); pixels=NULL;
  470.                 picframe=NULL;
  471.                 fprintf(stderr,"WARN: picframe disconnected\n");
  472.             }
  473.         }
  474.  
  475.         if (picframe==NULL)
  476.         {
  477.             char *dev=st2205dev;
  478.             if (dev==NULL)
  479.             {
  480.                 static char path[PATH_MAX];
  481.                 DIR *d=opendir("/dev/disk/by-id/");
  482.                 if (!d) return;
  483.        
  484.                 struct dirent *e;
  485.                 while ((e=readdir(d))!=NULL)
  486.                 {
  487.                     if (strstr(e->d_name,"SITRONIX")!=NULL)
  488.                     { sprintf(path,"/dev/disk/by-id/%s",e->d_name); break; }
  489.                 }
  490.                 closedir(d);
  491.                 if (e==NULL) return;
  492.                 dev=path;
  493.             }
  494.  
  495.             if (realpath(dev,device)==NULL)
  496.             { fprintf(stderr,"WARN: realpath failed to resolve device %s\n",dev); return; }
  497.  
  498.             fprintf(stderr,"INFO: connecting to picframe device %s (%s)\n",dev,device);
  499.        
  500.             struct stat s; // stat device
  501.             int rc=stat(device,&s);
  502.             if (rc>=0)
  503.             { // we're disconnected but device is there - see if we can connect
  504.                 picframe=st2205_open(device);
  505.                 if (picframe)
  506.                 {
  507.                     sw=picframe->width;
  508.                     sh=picframe->height;
  509.                     pixels=(unsigned char*)malloc(sw*sh*3);
  510.                     create_terminal();
  511.                     fprintf(stderr,"INFO: successfully connected to picframe\n");
  512.                 }
  513.                 else fprintf(stderr,"WARN: failed to connect to picframe\n");
  514.             }
  515.         }
  516. #else
  517.         fprintf(stderr,"ERROR: st2205 support is not compiled into binary!\n"); exit(1);
  518. #endif
  519.     }
  520. }
  521.  
  522. #ifdef HAVE_DLO
  523. static void check_for_scrolling(unsigned int *hash,unsigned int *lasthash,
  524.                                 unsigned char *lastbuf,unsigned char *lastattr,
  525.                                 int cx,int cy,font_t *font,RoteTerm *rt)
  526. {
  527.     int i,j,k,l;
  528.     for (k=-1,i=0;i<th;i++) // go through new screen
  529.     {
  530.         bad_row[i]=0; source_row[i]=-1;
  531.         if (lasthash[i]==hash[i]) continue; // line hasnt changed
  532.  
  533.         for (l=0;l<th;l++) // try to locate the row from the old screen for the new one
  534.         {
  535.             if (++k==th) k=0;
  536.             if (hash[i]!=lasthash[k]) continue;
  537.             if (k==i || k==cy) continue; // avoid self and cursor row
  538.  
  539.             unsigned char *b=lastbuf+k*tw,*a=lastattr+k*tw;
  540.             for (j=0;j<tw && rt->cells[i][j].ch==*b && rt->cells[i][j].attr==*a;j++,a++,b++) { }
  541.             if (j==tw) { source_row[i]=k; break; }
  542.         }
  543.     }
  544.     for (i=0;i<th;i++)
  545.     {
  546.         k=source_row[i];
  547.         if (k<0 || bad_row[k]) continue;
  548.  
  549.         int rows=1;
  550.         while (i+rows<th && source_row[i+rows]==k+rows) rows++;
  551.        
  552.         dlo_rect_t rec;
  553.         dlo_dot_t dot;
  554.         rec.origin.x=0;
  555.         rec.origin.y=k*font->h;
  556.         rec.width=tw*font->w;
  557.         rec.height=rows*font->h;
  558.         dot.x=0;
  559.         dot.y=i*font->h;
  560.         dlo_copy_rect(uid,NULL,&rec,NULL,&dot);
  561.  
  562.         unsigned char *bo=lastbuf+k*tw,*ao=lastattr+k*tw,*b=lastbuf+i*tw,*a=lastattr+i*tw;
  563.         memmove(b,bo,rows*tw);
  564.         memmove(a,ao,rows*tw);
  565.  
  566.         unsigned char *s=pixels+k*font->h*sw,*p=pixels+i*font->h*sw;
  567.         memmove(p,s,rows*font->h*sw);
  568.  
  569.         memset(&bad_row[i],1,rows);
  570.  
  571. //fprintf(stderr,"copy %d rows from y=%d to y=%d\n",rows,k,i);
  572.         i+=rows-1;
  573.     }
  574. }
  575. #endif
  576.  
  577. int main(int argc, char **argv)
  578. {
  579.     int verbose=0;
  580.     int fd=-1;
  581.     int font_num=0;
  582.     int fast_scroll=0;
  583.  
  584.     char *kbdev=NULL;
  585.     char *keymapfile=NULL;
  586.     char *fontsfile=NULL;
  587.  
  588.     int i,bad=0;
  589.     for (i=1;i<argc;i++)
  590.     {
  591.         if (strcmp(argv[i],"-v")==0 || strcmp(argv[i],"--verbose")==0) { verbose=1; continue; }
  592.         if (strcmp(argv[i],"--font=tiny")==0)  { font_num=0; continue; }
  593.         if (strcmp(argv[i],"--font=small")==0) { font_num=1; continue; }
  594.         if (strcmp(argv[i],"--font=large")==0) { font_num=2; continue; }
  595.         if (strcmp(argv[i],"--font=giant")==0) { font_num=3; continue; }
  596.         if (strncmp(argv[i],"--keymap=",9)==0) { keymapfile=argv[i]+9; continue; }
  597.         if (strncmp(argv[i],"--fontdata=",9)==0) { fontsfile=argv[i]+11; continue; }
  598.         if (strncmp(argv[i],"--picframe=",11)==0) { st2205dev=argv[i]+11; use_st2205=1; continue; }
  599.         if (strcmp(argv[i],"--dpf")==0) { use_dpf=1; continue; }
  600.         if (strcmp(argv[i],"--dlo")==0) { use_dlo=1; continue; }
  601.         if (strcmp(argv[i],"--dlo-fast-scroll=yes")==0) { fast_scroll=1; continue; }
  602.         if (strcmp(argv[i],"--dlo-fast-scroll=no")==0) { fast_scroll=0; continue; }
  603.         if (strncmp(argv[i],"--keyboard=",11)==0) { kbdev=argv[i]+11; continue; }
  604.         if (strcmp(argv[i],"--blink")==0) { blinking_cursor=1; continue; }
  605.         if (strncmp(argv[i],"--command=",10)==0) { command=argv[i]+10; continue; }
  606.         if (strncmp(argv[i],"--screensaver=",14)==0) { command2=argv[i]+14; continue; }
  607.         fprintf(stderr,"unrecognized command line option '%s'\n",argv[i]);
  608.         bad=1;
  609.         break;
  610.     }
  611.     if (use_st2205==0 && use_dlo==0) use_dpf=1;
  612.     if (keymapfile==NULL)
  613.     {
  614.         static char path[PATH_MAX];
  615.         if (realpath("/proc/self/exe",path)==NULL)
  616.         { fprintf(stderr,"ERROR: realpath(/proc/self/exe) failed!\n"); exit(1); }
  617.         int len=strlen(path);
  618.         while (--len>=0) { if (path[len]=='/') break; }
  619.         path[len--]=0;
  620.         strcat(path,"/us.kmap");
  621.         keymapfile=path;
  622.         fprintf(stderr,"INFO: using keymap from file %s\n",keymapfile);
  623.     }
  624.     if (fontsfile==NULL)
  625.     {
  626.         static char path[PATH_MAX];
  627.         if (realpath("/proc/self/exe",path)==NULL)
  628.         { fprintf(stderr,"ERROR: realpath(/proc/self/exe) failed!\n"); exit(1); }
  629.         int len=strlen(path);
  630.         while (--len>=0) { if (path[len]=='/') break; }
  631.         path[len--]=0;
  632.         strcat(path,"/fonts.txt");
  633.         fontsfile=path;
  634.         fprintf(stderr,"INFO: using fontdata from file %s\n",fontsfile);
  635.     }
  636.     if (use_dlo)
  637.         fprintf(stderr,"INFO: attempting to use Displaylink screen for output\n");
  638.     else if (st2205dev==NULL)
  639.         fprintf(stderr,"INFO: picframe selection automatic (polling /dev/disk/by-id)\n");
  640.  
  641.     if (keymapfile==NULL || fontsfile==NULL) bad=1;
  642.     if (bad)
  643.     {
  644.         fprintf(stderr,"usage: %s [--font=tiny|small|large|giant] "
  645.                        "[--command=<command>] [--screensaver=<command>] "
  646.                        "[--dlo|--picframe=/dev/sdb] [-dlo-fast-scroll=[yes|no]] [--blink] "
  647.                        "[--keyboard=/dev/input/event0] "
  648.                        "[--keymap=us.kmap] [--fontdata=fonts.txt]\n",argv[0]);
  649.         exit(1);
  650.     }
  651.  
  652.     load_kmap(keymapfile);
  653.  
  654.     font=load_font(fontsfile,font_num);
  655.  
  656.     struct input_event ev[64];
  657.     fd_set readset,*rs=NULL,exceptset,*es=NULL;
  658.     int ss_active=0;
  659.     time_t last_keypress=time(NULL);
  660.     for(;;)
  661.     {
  662.         check_for_output_device();
  663.  
  664.         if (fd<0) // no current keyboard => check if one was plugged in
  665.         {
  666.             fd=check_for_keyboard(kbdev);
  667.             if (fd>=0) { rs=&readset; FD_ZERO(rs); es=&exceptset; FD_ZERO(es); }
  668.         }
  669.  
  670.         // wait 200ms for keystrokes, or 1sec if keyboard or output device is unplugged
  671.         struct timeval timeout={2,0};
  672.         if (rs!=NULL && rt1!=NULL) { FD_SET(fd,rs); FD_SET(fd,es); timeout.tv_sec=0; timeout.tv_usec=200000; }
  673.         if (select(rs?fd+1:0,rs,NULL,es,&timeout)>0)
  674.         {
  675.             // something happened - keypress or keyboard disconnect
  676.             int rb=read(fd,ev,sizeof(ev));
  677.             if (rb<0 || FD_ISSET(fd,es))
  678.             {
  679.                 fprintf(stderr,"Keyboard unplugged\n");
  680.                 close(fd); fd=-1; rs=es=NULL;
  681.                 continue;
  682.             }
  683.  
  684.             // map and forward all the keystrokes to the terminal
  685.             if (rb<(int)sizeof(struct input_event)) { perror("evtest: short read"); exit (1); }
  686.             int e;
  687.             for (e=0;e<(int)(rb/sizeof(struct input_event));e++)
  688.             {
  689.                 if (ev[e].type!=EV_KEY) continue;
  690.  
  691.                 int code=ev[e].code;
  692.                 int pressed=ev[e].value;
  693.  
  694.                 int linuxkey=evkey2linuxkey(code,pressed);
  695.  
  696.                 if (verbose)
  697.                 {
  698.                     char *kn="????";
  699.                     if (code<MAX_EVKEYS) kn=kmap[code].keyname;
  700.                     fprintf(stderr,"%ld.%06ld evdev keycode %d keyname=%s is %s\n",
  701.                            ev[e].time.tv_sec,ev[e].time.tv_usec,code,kn,pressed?"down":"up");
  702.                     switch (linuxkey)
  703.                     {
  704.                     case -1: fprintf(stderr,"    special key\n"); break;
  705.                     case 0:  fprintf(stderr,"    no mapping\n"); break;
  706.                     default:
  707.                         fprintf(stderr,"    maps => %s linux key code 0x%02x (%c)\n",
  708.                                kmap[code].keyname,linuxkey,linuxkey>=32 && linuxkey<126?linuxkey:'?');
  709.                         break;
  710.                     }
  711.                 }
  712.                 if (linuxkey>0 && rt1!=NULL) rote_vt_keypress(rt1,linuxkey);
  713.                 ss_active=0;
  714.                 last_keypress=time(NULL);
  715.             }
  716.         }
  717.         if (rt1==NULL) continue;
  718.  
  719.         if (rt2!=NULL && !ss_active)
  720.             if (time(NULL)-last_keypress>=5)
  721.                 ss_active=1;
  722.  
  723.         RoteTerm *rt=ss_active?rt2:rt1;
  724.  
  725.         // redraw the terminal screen in memory
  726.         rote_vt_draw(rt,NULL,1,1,NULL);
  727.  
  728.         int i,j;
  729. #ifdef HAVE_DLO
  730.         // create hash of each terminal row to speed up finding changes
  731.         int h=0;
  732.         for (i=0;i<th;i++)
  733.         {
  734.             h=0;
  735.             for (j=0;j<tw;j++)
  736.                 h=(h<<5) + h + rt->cells[i][j].ch + rt->cells[i][j].attr;
  737.             hash[i]=h;
  738.         }
  739.         // displaylink supports moving areas on display
  740.         if (fast_scroll) check_for_scrolling(hash,lasthash,lastbuf,lastattr,last_cx,last_cy,font,rt);
  741.         // bounding box for screen changes
  742.         minx=sw; maxx=0; miny=sh; maxy=0;
  743. #endif
  744.  
  745.         // find screen changes
  746.         int count=0;
  747.         int cy=rt->crow,cx=rt->ccol;
  748.         for (i=0;i<th;i++)
  749.         {
  750. #ifdef HAVE_DLO
  751.             if (hash[i]==lasthash[i]) continue; // we'll risk hash collisions for efficiency
  752. #endif
  753.             unsigned char *b=lastbuf+i*tw,*a=lastattr+i*tw;
  754.             for (j=0;j<tw;j++)
  755.             {
  756.                 int ch=rt->cells[i][j].ch,attr=rt->cells[i][j].attr;
  757.                 if (ch!=*b || attr!=*a)
  758.                 {
  759.                     if (j==cx && i==cy)
  760.                         draw_char(pixels,j,i,ch,ROTE_ATTR_FG(attr),ROTE_ATTR_BG(attr),font,sh,sw); // inverse video for cursor
  761.                     else
  762.                         draw_char(pixels,j,i,ch,ROTE_ATTR_BG(attr),ROTE_ATTR_FG(attr),font,sh,sw);
  763.                     *b=ch; *a=attr;
  764.                     count++;
  765.                 }
  766.                 b++; a++;
  767.             }
  768.         }
  769.         int draw_cursor=0;
  770.         if (blinking_cursor)
  771.         {
  772.             struct timeval tv;
  773.             gettimeofday(&tv,NULL);
  774.             unsigned int elapsed_us=(tv.tv_sec-blink_last.tv_sec)*1000000+(tv.tv_usec-blink_last.tv_usec);
  775.             if (elapsed_us>500000)
  776.             {
  777.                 draw_cursor=1;
  778.                 blink_toggle=1-blink_toggle;
  779.                 blink_last.tv_sec=tv.tv_sec;
  780.                 blink_last.tv_usec=tv.tv_usec;
  781.             }
  782.         }
  783.         if (draw_cursor || cy!=last_cy || cx!=last_cx)
  784.         {
  785.             draw_char(pixels,last_cx,last_cy,rt->cells[last_cy][last_cx].ch,
  786.                       ROTE_ATTR_BG(rt->cells[last_cy][last_cx].attr),ROTE_ATTR_FG(rt->cells[last_cy][last_cx].attr),
  787.                       font,sh,sw);
  788.             if (!blinking_cursor || blink_toggle)
  789.                 draw_char(pixels,cx,cy,rt->cells[cy][cx].ch,
  790.                           ROTE_ATTR_FG(rt->cells[cy][cx].attr),ROTE_ATTR_BG(rt->cells[cy][cx].attr),
  791.                           font,sh,sw); // inverse video for cursor
  792.             last_cy=cy; last_cx=cx;
  793.             count++;
  794.         }
  795. #ifdef HAVE_DLO
  796.         memcpy(lasthash,hash,th*sizeof(lasthash[0]));
  797. #endif
  798.         if (!count) continue;
  799. //fprintf(stderr,"count=%d\n",count);
  800.  
  801.         #ifdef HAVE_ST2205
  802.         if (use_st2205 && picframe!=NULL) // push changes to picframe - copies entire display
  803.         {
  804.             if (picframe->oldpix) { free(picframe->oldpix); picframe->oldpix=NULL; } // avoid libst2205 incremental update bug
  805.             st2205_send_data(picframe,pixels);
  806.            
  807.  
  808.         }
  809.         #endif
  810.         #ifdef HAVE_DPF
  811.         if (use_dpf && picframe!=NULL) // push changes to picframe - copies entire display
  812.         {
  813.                 short rect[4];
  814.                 rect[0]=0;
  815.                 rect[1]=0;
  816.                 rect[2]=sw;
  817.                 rect[3]=sh;    
  818.                 dpf_screen_blit(picframe,pixels,rect);
  819.         }
  820.         #endif
  821.         #ifdef HAVE_DLO
  822.         if (use_dlo && !dlo_broken) // push changes to displaylink device
  823.         {
  824.             dlo_bmpflags_t flags = { 0 };
  825.             dlo_fbuf_t fbuf;
  826.             dlo_dot_t dot;
  827.  
  828.             fbuf.width=maxx-minx;
  829.             fbuf.height=maxy-miny;
  830.             fbuf.base=pixels+miny*sw+minx;
  831.             fbuf.stride=sw;
  832.             fbuf.fmt=dlo_pixfmt_rgb323;
  833.             dot.x=minx;
  834.             dot.y=miny;
  835.             if (dlo_copy_host_bmp(uid,flags,&fbuf,NULL,&dot)!=dlo_ok)
  836.  
  837.                 dlo_broken=1;
  838.         }
  839.         #endif
  840.     }
  841.     return 0;
  842. }