raster pushed a commit to branch master.

http://git.enlightenment.org/apps/terminology.git/commit/?id=125d4750683141395bb5de38e4c415d31c5ed8c9

commit 125d4750683141395bb5de38e4c415d31c5ed8c9
Author: Carsten Haitzler (Rasterman) <[email protected]>
Date:   Sun Dec 17 23:36:51 2017 +0900

    add tysend cmdline and support in escapes for sending single files
    
    this allows you to send a file via escapes to terminology which will
    pop up a file save dialog and ask what to save it as and where (based
    on the original name). terminology will show a progress bar too. this
    is not useful locally but remotely (e.g. you ssh'd into another
    machine) it's a VERY handy way of fetching a file from the remote
    machine to the local machine with a display just with tysend FILE. you
    can send multiple in a sequence with tysend FILE1 FILE2 FILE3 ... ...
    and terminology will ask for a location and filename per file you send
    (just hit cancel if you don't want to do it).
    
    note - it needs new theme features to work. it'll fail without them.
    
    @feature
---
 README                  |  19 +++
 data/themes/default.edc | 165 +++++++++++++++++++++++++
 src/bin/meson.build     |   6 +
 src/bin/termio.c        | 196 ++++++++++++++++++++++++++++++
 src/bin/termio.h        |   3 +
 src/bin/tyalpha.c       |   2 +-
 src/bin/tybg.c          |   4 +-
 src/bin/tycat.c         |   6 +-
 src/bin/tyls.c          |   2 +-
 src/bin/typop.c         |   2 +-
 src/bin/tyq.c           |   2 +-
 src/bin/tysend.c        | 149 +++++++++++++++++++++++
 src/bin/win.c           | 314 +++++++++++++++++++++++++++++++++++++++++++++++-
 13 files changed, 860 insertions(+), 10 deletions(-)

diff --git a/README b/README
index 510f384..5e53487 100644
--- a/README
+++ b/README
@@ -299,3 +299,22 @@ ib
 
 ie
   = end media replace sequence run
+
+fr[PATH/FILE]
+  = begin file send for a file named PATH/FILE
+
+fs[SIZE_BYTES]
+  = set the size in bytes of a file send started with the above fr escape
+
+fd[CHECKSUM DATA]
+  = block of data for the current file transfer with checksum as a
+    string decimal which is the sum of every byte when taken as an
+    unsigned char per byte. the checksum is a signed 32bit integer.
+    the checksum is the sum of the data after escaping. data will be
+    escaped using a 0xff byte as the escape header. the escape sequence
+    of 0xff 0x01 represents a 0x00 (nul) bytes, and 0xff 0x02 represents
+    a 0xff byte. all other bytes are transitted as-is.
+
+fx
+  = exit file send mode (normally at the end of the file or when it's
+    complete)
diff --git a/data/themes/default.edc b/data/themes/default.edc
index c794f28..ba4e90d 100644
--- a/data/themes/default.edc
+++ b/data/themes/default.edc
@@ -652,6 +652,171 @@ collections {
          }
 
          ////////////////////////////////////////////////////////////////////
+         // sendfile request
+         part { name: "sendfile_request_clip"; type: RECT;
+            description { state: "default" 0.0;
+               color: 255 255 255 0;
+               visible: 0;
+            }
+            description { state: "on" 0.0;
+               inherit: "default" 0.0;
+               visible: 1;
+               color: 255 255 255 255;
+            }
+         }
+         part { name: "sendfile_request_shadow";
+            mouse_events: 0;
+            clip_to: "sendfile_request_clip";
+            description { state: "default" 0.0;
+               fixed: 1 1;
+               rel.to: "sendfile_request_bg";
+               rel1.offset: -32 -32;
+               rel2.offset: 31 31;
+               image.normal: "pm_shadow.png";
+               image.border: 64 64 64 64;
+               fill.smooth: 0;
+            }
+         }
+         part { name: "sendfile_request_bg"; type: RECT;
+            clip_to: "sendfile_request_clip";
+            description { state: "default" 0.0;
+               color: 64 64 64 255;
+               rel1.relative: 0.0 -1.0;
+               rel2.relative: 1.0 0.0;
+            }
+            description { state: "on" 0.0;
+               inherit: "default" 0.0;
+               rel1.relative: 0.0 0.0;
+               rel2.relative: 1.0 1.0;
+            }
+         }
+         part { name: "terminology.sendfile.request"; type: SWALLOW;
+            clip_to: "sendfile_request_clip";
+            scale: 1;
+            description { state: "default" 0.0;
+               rel.to: "sendfile_request_bg";
+               rel1.offset: 4 4;
+               rel2.offset: -5 -5;
+               offscale;
+            }
+            description { state: "on" 0.0;
+               inherit: "default" 0.0;
+               rel1.relative: 0.0 0.0;
+               rel2.relative: 1.0 1.0;
+            }
+         }
+         program {
+            signal: "sendfile,request,on"; source: "terminology";
+            action: ACTION_STOP;
+            target: "sendfile_request_on";
+            target: "sendfile_request_off";
+         }
+         program {
+            signal: "sendfile,request,off"; source: "terminology";
+            action: ACTION_STOP;
+            target: "sendfile_request_on";
+            target: "sendfile_request_off";
+         }
+         program { name: "sendfile_request_on";
+            signal: "sendfile,request,on"; source: "terminology";
+            in: 0.5 0.0;
+            action: STATE_SET "on" 0.0;
+            transition: DECELERATE 0.5;
+            target: "sendfile_request_clip";
+            target: "sendfile_request_bg";
+         }
+         program { name: "sendfile_request_off";
+            signal: "sendfile,request,off"; source: "terminology";
+            action: STATE_SET "default" 0.0;
+            transition: DECELERATE 0.5;
+            target: "sendfile_request_clip";
+            target: "sendfile_request_bg";
+         }
+
+         ////////////////////////////////////////////////////////////////////
+         // sendfile progress
+         part { name: "sendfile_progress_clip"; type: RECT;
+            description { state: "default" 0.0;
+               color: 255 255 255 0;
+               visible: 0;
+            }
+            description { state: "on" 0.0;
+               inherit: "default" 0.0;
+               visible: 1;
+               color: 255 255 255 255;
+            }
+         }
+         part { name: "sendfile_progress_shadow";
+            mouse_events: 0;
+            clip_to: "sendfile_progress_clip";
+            description { state: "default" 0.0;
+               fixed: 1 1;
+               rel.to: "sendfile_progress_bg";
+               rel1.offset: -32 -32;
+               rel2.offset: 31 31;
+               image.normal: "pm_shadow.png";
+               image.border: 64 64 64 64;
+               fill.smooth: 0;
+            }
+         }
+         part { name: "sendfile_progress_bg"; type: RECT;
+            clip_to: "sendfile_progress_clip";
+            scale : 1;
+            description { state: "default" 0.0;
+               color: 64 64 64 255;
+               rel.to: "terminology.sendfile.progress";
+               rel1.offset: -4 -4;
+               rel2.offset: 4 4;
+               offscale;
+            }
+         }
+         part { name: "terminology.sendfile.progress"; type: SWALLOW;
+            clip_to: "sendfile_progress_clip";
+            scale : 1;
+            description { state: "default" 0.0;
+               rel1.relative: 0.0 0.0;
+               rel1.offset: 4 -5;
+               rel2.relative: 1.0 0.0;
+               rel2.offset: -5 -5;
+               align: 0.5 1.0;
+               offscale;
+            }
+            description { state: "on" 0.0;
+               inherit: "default" 0.0;
+               rel1.offset: 4 4;
+               rel2.offset: -5 4;
+               align: 0.5 0.0;
+            }
+         }
+         program {
+            signal: "sendfile,progress,on"; source: "terminology";
+            action: ACTION_STOP;
+            target: "sendfile_progress_on";
+            target: "sendfile_progress_off";
+         }
+         program {
+            signal: "sendfile,progress,off"; source: "terminology";
+            action: ACTION_STOP;
+            target: "sendfile_progress_on";
+            target: "sendfile_progress_off";
+         }
+         program { name: "sendfile_progress_on";
+            signal: "sendfile,progress,on"; source: "terminology";
+            in: 0.5 0.0;
+            action: STATE_SET "on" 0.0;
+            transition: DECELERATE 0.5;
+            target: "sendfile_progress_clip";
+            target: "terminology.sendfile.progress";
+         }
+         program { name: "sendfile_progress_off";
+            signal: "sendfile,progress,off"; source: "terminology";
+            action: STATE_SET "default" 0.0;
+            transition: DECELERATE 0.5;
+            target: "sendfile_progress_clip";
+            target: "terminology.sendfile.progress";
+         }
+
+         ////////////////////////////////////////////////////////////////////
          // miniview
          part { name: "terminology.miniview"; type: SWALLOW;
             description { state: "default" 0.0;
diff --git a/src/bin/meson.build b/src/bin/meson.build
index 224470f..97b33bf 100644
--- a/src/bin/meson.build
+++ b/src/bin/meson.build
@@ -47,6 +47,7 @@ typop_sources = ['tycommon.c', 'tycommon.h', 'typop.c']
 tyq_sources = ['tycommon.c', 'tycommon.h', 'tyq.c']
 tycat_sources = ['tycommon.c', 'tycommon.h', 'tycat.c', 'extns.c', 'extns.h']
 tyls_sources = ['extns.c', 'extns.h', 'tyls.c', 'tycommon.c', 'tycommon.h']
+tysend_sources = ['tycommon.c', 'tycommon.h', 'tysend.c']
 tyfuzz_sources = ['termptyesc.c', 'termptyesc.h',
                   'termptysave.c', 'termptysave.h',
                   'termptyops.c', 'termptyops.h',
@@ -94,6 +95,11 @@ executable('tyls',
            install: true,
            include_directories: config_dir,
            dependencies: terminology_dependencies)
+executable('tysend',
+           tysend_sources,
+           install: true,
+           include_directories: config_dir,
+           dependencies: terminology_dependencies)
 
 if fuzzing
   executable('tyfuzz',
diff --git a/src/bin/termio.c b/src/bin/termio.c
index ca488b0..b5f60a8 100644
--- a/src/bin/termio.c
+++ b/src/bin/termio.c
@@ -62,6 +62,13 @@ struct _Termio
          unsigned char dndobjdel : 1;
       } down;
    } link;
+   struct {
+      const char *file;
+      FILE *f;
+      double progress;
+      unsigned long long total, size;
+      Eina_Bool active : 1;
+   } sendfile;
    Evas_Object *ctxpopup;
    int zoom_fontsize_start;
    int scroll;
@@ -4729,6 +4736,70 @@ _smart_cb_gest_zoom_abort(void *data, void *_event 
EINA_UNUSED)
 }
 
 /* }}} */
+
+Eina_Bool
+termio_file_send_ok(const Evas_Object *obj, const char *file)
+{
+   Termio *sd = evas_object_smart_data_get(obj);
+   Termpty *ty;
+
+   if (!sd) return EINA_FALSE;
+   if (!file) return EINA_FALSE;
+   ty = sd->pty;
+   sd->sendfile.f = fopen(file, "w");
+   if (sd->sendfile.f)
+     {
+        if (sd->sendfile.file) eina_stringshare_del(sd->sendfile.file);
+        sd->sendfile.file = eina_stringshare_add(file);
+        sd->sendfile.active = EINA_TRUE;
+        termpty_write(ty, "k\n", 2);
+        return EINA_TRUE;
+     }
+   if (sd->sendfile.file) eina_stringshare_del(sd->sendfile.file);
+   sd->sendfile.file = NULL;
+   sd->sendfile.active = EINA_FALSE;
+   termpty_write(ty, "n\n", 2);
+   return EINA_FALSE;
+}
+
+void
+termio_file_send_cancel(const Evas_Object *obj)
+{
+   Termio *sd = evas_object_smart_data_get(obj);
+   Termpty *ty;
+
+   if (!sd) return;
+   ty = sd->pty;
+   if (!sd->sendfile.active) goto done;
+   sd->sendfile.progress = 0.0;
+   sd->sendfile.total = 0;
+   sd->sendfile.size = 0;
+   if (sd->sendfile.file)
+     {
+        ecore_file_unlink(sd->sendfile.file);
+        eina_stringshare_del(sd->sendfile.file);
+        sd->sendfile.file = NULL;
+     }
+   if (sd->sendfile.f)
+     {
+        fclose(sd->sendfile.f);
+        sd->sendfile.f = NULL;
+     }
+   sd->sendfile.active = EINA_FALSE;
+done:
+   termpty_write(ty, "n\n", 2);
+}
+
+double
+termio_file_send_progress_get(const Evas_Object *obj)
+{
+   Termio *sd = evas_object_smart_data_get(obj);
+
+   if (!sd) return 0.0;
+   if (!sd->sendfile.active) return 0.0;
+   return sd->sendfile.progress;
+}
+
 /* {{{ Smart */
 
 static void
@@ -5431,6 +5502,21 @@ _smart_del(Evas_Object *obj)
         evas_object_del(o);
      }
    if (sd->link.down.dndobj) evas_object_del(sd->link.down.dndobj);
+   if (sd->sendfile.active)
+     {
+        if (sd->sendfile.file)
+          {
+             ecore_file_unlink(sd->sendfile.file);
+             eina_stringshare_del(sd->sendfile.file);
+             sd->sendfile.file = NULL;
+          }
+        if (sd->sendfile.f)
+          {
+             fclose(sd->sendfile.f);
+             sd->sendfile.f = NULL;
+          }
+        sd->sendfile.active = EINA_FALSE;
+     }
    keyin_compose_seq_reset(&sd->khdl);
    if (sd->sel_str) eina_stringshare_del(sd->sel_str);
    if (sd->preedit_str) eina_stringshare_del(sd->preedit_str);
@@ -5927,6 +6013,116 @@ _smart_pty_command(void *data)
              return;
           }
      }
+   else if (ty->cur_cmd[0] == 'f') // file...
+     {
+        if (ty->cur_cmd[1] == 'r') // receive
+          {
+             sd->sendfile.progress = 0.0;
+             sd->sendfile.total = 0;
+             sd->sendfile.size = 0;
+          }
+        else if (ty->cur_cmd[1] == 's') // file size
+          {
+             sd->sendfile.total = 0;
+             sd->sendfile.size = atoll(&(ty->cur_cmd[2]));
+          }
+        else if (ty->cur_cmd[1] == 'd') // data packet
+          {
+             int pksum = atoi(&(ty->cur_cmd[2]));
+             int sum;
+             char *p = strchr(ty->cur_cmd, ' ');
+             Eina_Bool valid = EINA_TRUE;
+
+             if (p)
+               {
+                  Eina_Binbuf *bb = eina_binbuf_new();
+                  unsigned char v;
+                  int inp = 0;
+
+                  if (bb)
+                    {
+                       p++;
+                       sum = 0;
+                       for (; *p; p++)
+                         {
+                            v = (unsigned char)(*p);
+                            sum += v;
+                            inp++;
+                            if ((v == 0x1b) || (v == 0x07))
+                              {
+                                 p++;
+                                 v = *p;
+                                 inp++;
+                                 sum += (unsigned char)(*p);
+                                 if      (*p == 0x01) v = 0x00;
+                                 else if (*p == 0x02) v = 0xff;
+                                 else valid = EINA_FALSE;
+                              }
+                            eina_binbuf_append_char(bb, v);
+                         }
+                       if ((valid) && (sum == pksum) && (sd->sendfile.active))
+                         {
+                            // write "ok" (k) to term
+                            size_t size = eina_binbuf_length_get(bb);
+
+                            sd->sendfile.total += size;
+                            if (sd->sendfile.size > 0.0)
+                              {
+                                 sd->sendfile.progress =
+                                   (double)sd->sendfile.total /
+                                   (double)sd->sendfile.size;
+                                 evas_object_smart_callback_call
+                                   (obj, "send,progress", NULL);
+                              }
+                            fwrite(eina_binbuf_string_get(bb), size, 1,
+                                   sd->sendfile.f);
+                            termpty_write(ty, "k\n", 2);
+                         }
+                       else
+                         {
+                            // write "not valid" (n) to term
+                            if (sd->sendfile.file)
+                              {
+                                 ecore_file_unlink(sd->sendfile.file);
+                                 eina_stringshare_del(sd->sendfile.file);
+                                 sd->sendfile.file = NULL;
+                              }
+                            if (sd->sendfile.f)
+                              {
+                                 fclose(sd->sendfile.f);
+                                 sd->sendfile.f = NULL;
+                              }
+                            sd->sendfile.active = EINA_FALSE;
+                            termpty_write(ty, "n\n", 2);
+                            evas_object_smart_callback_call
+                              (obj, "send,end", NULL);
+                         }
+                       eina_binbuf_free(bb);
+                    }
+               }
+          }
+        else if (ty->cur_cmd[1] == 'x') // exit data stream
+          {
+             if (sd->sendfile.active)
+               {
+                  sd->sendfile.progress = 0.0;
+                  sd->sendfile.size = 0;
+                  if (sd->sendfile.file)
+                    {
+                       eina_stringshare_del(sd->sendfile.file);
+                       sd->sendfile.file = NULL;
+                    }
+                  if (sd->sendfile.f)
+                    {
+                       fclose(sd->sendfile.f);
+                       sd->sendfile.f = NULL;
+                    }
+                  sd->sendfile.active = EINA_FALSE;
+                  evas_object_smart_callback_call
+                    (obj, "send,end", NULL);
+               }
+          }
+     }
    evas_object_smart_callback_call(obj, "command", (void *)ty->cur_cmd);
 }
 
diff --git a/src/bin/termio.h b/src/bin/termio.h
index 32eb459..f930393 100644
--- a/src/bin/termio.h
+++ b/src/bin/termio.h
@@ -45,6 +45,9 @@ void         termio_media_mute_set(Evas_Object *obj, 
Eina_Bool mute);
 void         termio_media_visualize_set(Evas_Object *obj, Eina_Bool visualize);
 void         termio_config_set(Evas_Object *obj, Config *config);
 Config      *termio_config_get(const Evas_Object *obj);
+Eina_Bool    termio_file_send_ok(const Evas_Object *obj, const char *file);
+void         termio_file_send_cancel(const Evas_Object *obj);
+double       termio_file_send_progress_get(const Evas_Object *obj);
 
 Termpty *termio_pty_get(const Evas_Object *obj);
 Evas_Object * termio_miniview_get(const Evas_Object *obj);
diff --git a/src/bin/tyalpha.c b/src/bin/tyalpha.c
index 8b0223b..e3c4c83 100644
--- a/src/bin/tyalpha.c
+++ b/src/bin/tyalpha.c
@@ -46,7 +46,7 @@ main(int argc, char **argv)
           snprintf(tbuf, sizeof(tbuf), "%c}ap%s", 0x1b, argv[i]);
         else
           snprintf(tbuf, sizeof(tbuf), "%c}at%s", 0x1b, argv[i]);
-        if (write(0, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1)) 
perror("write");
+        if (write(1, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1)) 
perror("write");
      }
    return 0;
 }
diff --git a/src/bin/tybg.c b/src/bin/tybg.c
index 595d090..d2b7aaa 100644
--- a/src/bin/tybg.c
+++ b/src/bin/tybg.c
@@ -31,7 +31,7 @@ main(int argc, char **argv)
      {
         char tbuf[32];
         snprintf(tbuf, sizeof(tbuf), "%c}bt", 0x1b);
-        if (write(0, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1)) 
perror("write");
+        if (write(1, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1)) 
perror("write");
         return 0;
      }
    for (i = 1; i < argc; i++)
@@ -50,7 +50,7 @@ main(int argc, char **argv)
           snprintf(tbuf, sizeof(tbuf), "%c}bp%s", 0x1b, path);
         else
           snprintf(tbuf, sizeof(tbuf), "%c}bt%s", 0x1b, path);
-        if (write(0, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1)) 
perror("write");
+        if (write(1, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1)) 
perror("write");
      }
    return 0;
 }
diff --git a/src/bin/tycat.c b/src/bin/tycat.c
index 690650b..988c141 100644
--- a/src/bin/tycat.c
+++ b/src/bin/tycat.c
@@ -97,7 +97,7 @@ prnt(const char *path, int w, int h, int mode)
      snprintf(buf, sizeof(buf), "%c}if#%i;%i;%s", 0x1b, w, h, path);
    else
      snprintf(buf, sizeof(buf), "%c}is#%i;%i;%s", 0x1b, w, h, path);
-   if (write(0, buf, strlen(buf) + 1) < 0) perror("write");
+   if (write(1, buf, strlen(buf) + 1) < 0) perror("write");
    i = 0;
    line[i++] = 0x1b;
    line[i++] = '}';
@@ -113,7 +113,7 @@ prnt(const char *path, int w, int h, int mode)
    line[i++] = '\n';
    for (y = 0; y < h; y++)
      {
-        if (write(0, line, i) < 0) perror("write");
+        if (write(1, line, i) < 0) perror("write");
      }
    free(line);
 }
@@ -336,7 +336,7 @@ main(int argc, char **argv)
    evas = ecore_evas_get(ee);
    echo_off();
    snprintf(buf, sizeof(buf), "%c}qs", 0x1b);
-   if (write(0, buf, strlen(buf) + 1) < 0) perror("write");
+   if (write(1, buf, strlen(buf) + 1) < 0) perror("write");
    if (scanf("%i;%i;%i;%i", &tw, &th, &cw, &ch) != 4 ||
        ((tw <= 0) || (th <= 0) || (cw <= 1) || (ch <= 1)))
      {
diff --git a/src/bin/tyls.c b/src/bin/tyls.c
index 0ec6809..6f15813 100644
--- a/src/bin/tyls.c
+++ b/src/bin/tyls.c
@@ -765,7 +765,7 @@ main(int argc, char **argv)
         echo_off();
         snprintf(buf, sizeof(buf), "%c}qs", 0x1b);
         len = strlen(buf);
-        if (write(0, buf, len + 1) < (signed)len + 1) perror("write");
+        if (write(1, buf, len + 1) < (signed)len + 1) perror("write");
         if ((scanf("%i;%i;%i;%i", &tw, &th, &cw, &ch) != 4)
             || (tw <= 0) || (th <= 0) || (cw <= 1) || (ch <= 1))
           {
diff --git a/src/bin/typop.c b/src/bin/typop.c
index 0466d1d..f314c91 100644
--- a/src/bin/typop.c
+++ b/src/bin/typop.c
@@ -39,7 +39,7 @@ main(int argc, char **argv)
         path = argv[i];
         if (realpath(path, buf)) path = buf;
         snprintf(tbuf, sizeof(tbuf), "%c}pn%s", 0x1b, path);
-        if (write(0, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1)) 
perror("write");
+        if (write(1, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1)) 
perror("write");
      }
    return 0;
 }
diff --git a/src/bin/tyq.c b/src/bin/tyq.c
index 95ca8a7..aeae097 100644
--- a/src/bin/tyq.c
+++ b/src/bin/tyq.c
@@ -39,7 +39,7 @@ main(int argc, char **argv)
         path = argv[i];
         if (realpath(path, buf)) path = buf;
         snprintf(tbuf, sizeof(tbuf), "%c}pq%s", 0x1b, path);
-        if (write(0, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1)) 
perror("write");
+        if (write(1, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1)) 
perror("write");
      }
    return 0;
 }
diff --git a/src/bin/tysend.c b/src/bin/tysend.c
new file mode 100644
index 0000000..1bb2d78
--- /dev/null
+++ b/src/bin/tysend.c
@@ -0,0 +1,149 @@
+#include "private.h"
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#include <Eina.h>
+#include "tycommon.h"
+
+static void
+print_usage(const char *argv0)
+{
+   printf("Usage: %s"HELP_ARGUMENT_SHORT" FILE1 [FILE2 ...]\n"
+          "  Send file(s) to the terminal to save\n"
+          HELP_ARGUMENT_DOC"\n"
+          "\n",
+          argv0);
+}
+
+static struct termios told, tnew;
+
+static int
+echo_off(void)
+{
+   if (tcgetattr(0, &told) != 0) return -1;
+   tnew = told;
+   tnew.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+   tnew.c_oflag &= ~(OPOST);
+   tnew.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN);
+   tnew.c_cflag &= ~(CSIZE | PARENB);
+   tnew.c_cflag |= CS8;
+   tnew.c_cc[VMIN] = 1;
+   tnew.c_cc[VTIME] = 0;
+   if (tcsetattr(0, TCSAFLUSH, &tnew) != 0) return -1;
+   return 0;
+}
+
+static int
+echo_on(void)
+{
+   return tcsetattr(0, TCSAFLUSH, &told);
+}
+
+int
+main(int argc, char **argv)
+{
+   int i;
+
+   ON_NOT_RUNNING_IN_TERMINOLOGY_EXIT_1();
+   ARGUMENT_ENTRY_CHECK(argc, argv, print_usage);
+
+   if (argc <= 1)
+     {
+        print_usage(argv[0]);
+        return 0;
+     }
+
+   echo_off();
+   for (i = 1; i < argc; i++)
+     {
+        char *path, buf[8192], tbuf[PATH_MAX * 3];
+        unsigned char rawbuf[8192 + 128], rawbuf2[8192 + 128];
+        int file_fd, pksize, pksum, bin, bout;
+
+        path = argv[i];
+        snprintf(tbuf, sizeof(tbuf), "%c}fr%s", 0x1b, path);
+        if (write(1, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1))
+          goto err;
+        file_fd = open(path, O_RDONLY);
+        if (file_fd >= 0)
+          {
+             off_t off;
+
+             off = lseek(file_fd, 0, SEEK_END);
+             lseek(file_fd, 0, SEEK_SET);
+             snprintf(tbuf, sizeof(tbuf), "%c}fs%llu", 0x1b, (unsigned long 
long)off);
+             if (write(1, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 
1))
+               goto err;
+             for (;;)
+               {
+                  if (read(0, buf, 2) == 2)
+                    {
+                       if (buf[0] == 'k')
+                         {
+                            pksize = read(file_fd, rawbuf, 8192);
+
+                            if (pksize > 0)
+                              {
+                                 bout = 0;
+                                 for (bin = 0; bin < pksize; bin++)
+                                   {
+                                      if (rawbuf[bin] == 0x00)
+                                        {
+                                           rawbuf2[bout++] = 0xff;
+                                           rawbuf2[bout++] = 0x01;
+                                        }
+                                      else if (rawbuf[bin] == 0xff)
+                                        {
+                                           rawbuf2[bout++] = 0xff;
+                                           rawbuf2[bout++] = 0x02;
+                                        }
+                                      else
+                                        {
+                                           rawbuf2[bout++] = rawbuf[bin];
+                                        }
+                                   }
+                                 rawbuf2[bout] = 0;
+                                 pksum = 0;
+                                 for (bin = 0; bin < bout; bin++)
+                                   {
+                                      pksum += rawbuf2[bin];
+                                   }
+                                 snprintf(tbuf, sizeof(tbuf), "%c}fd%i ", 
0x1b, pksum);
+                                 if (write(1, tbuf, strlen(tbuf)) != 
(signed)(strlen(tbuf)))
+                                   goto err;
+                                 if (write(1, rawbuf2, bout + 1) != bout + 1)
+                                   goto err;
+                              }
+                            else break;
+                         }
+                       else
+                         {
+                            echo_on();
+                            fprintf(stderr, "Send Fail\n");
+                            goto err;
+                         }
+                    }
+                  else goto err;
+               }
+             close(file_fd);
+          }
+        snprintf(tbuf, sizeof(tbuf), "%c}fx", 0x1b);
+        if (write(1, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1))
+          goto err;
+        tbuf[0] = 0;
+        if (write(1, tbuf, 1) != 1)
+          goto err;
+     }
+   echo_on();
+   return 0;
+err:
+   echo_on();
+   return -1;
+}
diff --git a/src/bin/win.c b/src/bin/win.c
index 610f8b7..65a41b2 100644
--- a/src/bin/win.c
+++ b/src/bin/win.c
@@ -88,11 +88,17 @@ struct _Term
    Evas_Object *popmedia;
    Evas_Object *miniview;
    Evas_Object *sel;
+   Evas_Object *sendfile_request;
+   Evas_Object *sendfile_progress;
+   Evas_Object *sendfile_progress_bar;
    Evas_Object *tabcount_spacer;
    Evas_Object *tab_spacer;
    Evas_Object *tab_region_base;
    Evas_Object *tab_region_bg;
    Eina_List   *popmedia_queue;
+   Ecore_Timer *sendfile_request_hide_timer;
+   Ecore_Timer *sendfile_progress_hide_timer;
+   const char  *sendfile_dir;
    Media_Type   poptype, mediatype;
    Tabbar       tabbar;
    int          step_x, step_y, min_w, min_h, req_w, req_h;
@@ -105,6 +111,9 @@ struct _Term
    unsigned char missed_bell : 1;
    unsigned char miniview_shown : 1;
    unsigned char popmedia_deleted : 1;
+
+   Eina_Bool sendfile_request_enabled : 1;
+   Eina_Bool sendfile_progress_enabled : 1;
 };
 
 typedef struct _Solo Solo;
@@ -3734,6 +3743,247 @@ _set_alpha(Config *config, const char *val, Eina_Bool 
save)
 }
 
 static void
+_sendfile_progress_del(void *data EINA_UNUSED, Evas *e EINA_UNUSED, 
Evas_Object *obj, void *info EINA_UNUSED)
+{
+   Evas_Object *o = obj;
+   Term *term = evas_object_data_get(o, "sendfile-progress-term");
+   Ecore_Timer *t;
+
+   evas_object_data_del(o, "sendfile-progress-term");
+   if (term)
+     {
+        term->sendfile_progress = NULL;
+        term->sendfile_progress_bar = NULL;
+     }
+   t = evas_object_data_get(o, "sendfile-progress-timer");
+   evas_object_data_del(o, "sendfile-progress-term");
+   if (t) ecore_timer_del(t);
+}
+
+static Eina_Bool
+_sendfile_progress_reset(void *data)
+{
+   Evas_Object *o = data;
+   Term *term = evas_object_data_get(o, "sendfile-progress-term");
+
+   if (term)
+     {
+        term->sendfile_progress = NULL;
+        term->sendfile_progress_bar = NULL;
+     }
+   evas_object_data_del(o, "sendfile-progress-timer");
+   evas_object_data_del(o, "sendfile-progress-term");
+   evas_object_del(o);
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_sendfile_progress_hide_delay(void *data)
+{
+   Term *term = data;
+   Ecore_Timer *t;
+
+   term->sendfile_progress_hide_timer = NULL;
+   if (!term->sendfile_progress_enabled) return EINA_FALSE;
+   term->sendfile_progress_enabled = EINA_FALSE;
+   edje_object_signal_emit(term->bg, "sendfile,progress,off", "terminology");
+   t = evas_object_data_get(term->sendfile_progress, 
"sendfile-progress-timer");
+   if (t) ecore_timer_del(t);
+   t = ecore_timer_add(10.0, _sendfile_progress_reset, 
term->sendfile_progress);
+   evas_object_data_set(term->sendfile_progress, "sendfile-progress-timer", t);
+   return EINA_FALSE;
+}
+
+static void
+_sendfile_progress_hide(Term *term)
+{
+   if (!term->sendfile_progress_enabled) return;
+   if (term->sendfile_progress_hide_timer)
+     ecore_timer_del(term->sendfile_progress_hide_timer);
+   term->sendfile_progress_hide_timer =
+     ecore_timer_add(0.5, _sendfile_progress_hide_delay, term);
+}
+
+static void
+_sendfile_progress_cancel(void *data, Evas_Object *obj EINA_UNUSED, void *info 
EINA_UNUSED)
+{
+   Term *term = data;
+
+   if (!term->sendfile_progress) return;
+   termio_file_send_cancel(term->termio);
+   _sendfile_progress_hide(term);
+}
+
+static void
+_sendfile_progress(Term *term)
+{
+   Evas_Object *o, *base;
+
+   if (term->sendfile_progress)
+     {
+        evas_object_del(term->sendfile_progress);
+        term->sendfile_progress = NULL;
+     }
+   if (!edje_object_part_exists(term->bg, "terminology.sendfile.progress"))
+     {
+        return;
+     }
+   if (term->sendfile_progress_hide_timer)
+     {
+        ecore_timer_del(term->sendfile_progress_hide_timer);
+        term->sendfile_progress_hide_timer = NULL;
+     }
+   o = elm_box_add(term->wn->win);
+   evas_object_data_set(o, "sendfile-progress-term", term);
+   base = o;
+   term->sendfile_progress = o;
+   evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, 
_sendfile_progress_del, NULL);
+   elm_box_horizontal_set(o, EINA_TRUE);
+
+   o = elm_button_add(term->wn->win);
+   elm_object_text_set(o, "Cancel");
+   evas_object_smart_callback_add(o, "clicked", _sendfile_progress_cancel, 
term);
+   evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
+   elm_box_pack_end(base, o);
+   evas_object_show(o);
+
+   o = elm_progressbar_add(term->wn->win);
+   term->sendfile_progress_bar = o;
+   elm_progressbar_unit_format_set(o, "%1.0f%%");
+   evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+   evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
+   elm_box_pack_end(base, o);
+   evas_object_show(o);
+
+   term->sendfile_progress_enabled = EINA_TRUE;
+   edje_object_part_swallow(term->bg, "terminology.sendfile.progress", base);
+   evas_object_show(base);
+   edje_object_signal_emit(term->bg, "sendfile,progress,on", "terminology");
+}
+
+static void
+_sendfile_request_del(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object 
*obj, void *info EINA_UNUSED)
+{
+   Evas_Object *o = obj;
+   Term *term = evas_object_data_get(o, "sendfile-request-term");
+   Ecore_Timer *t;
+
+   evas_object_data_del(o, "sendfile-request-term");
+   if (term) term->sendfile_request = NULL;
+   t = evas_object_data_get(o, "sendfile-request-timer");
+   evas_object_data_del(o, "sendfile-request-term");
+   if (t) ecore_timer_del(t);
+}
+
+static Eina_Bool
+_sendfile_request_reset(void *data)
+{
+   Evas_Object *o = data;
+   Term *term = evas_object_data_get(o, "sendfile-request-term");
+
+   if (term) term->sendfile_request = NULL;
+   evas_object_data_del(o, "sendfile-request-timer");
+   evas_object_data_del(o, "sendfile-request-term");
+   evas_object_del(o);
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_sendfile_request_hide_delay(void *data)
+{
+   Term *term = data;
+   Ecore_Timer *t;
+
+   term->sendfile_request_hide_timer = NULL;
+   if (!term->sendfile_request_enabled) return EINA_FALSE;
+   term->sendfile_request_enabled = EINA_FALSE;
+   edje_object_signal_emit(term->bg, "sendfile,request,off", "terminology");
+   t = evas_object_data_get(term->sendfile_request, "sendfile-request-timer");
+   if (t) ecore_timer_del(t);
+   t = ecore_timer_add(10.0, _sendfile_request_reset, term->sendfile_request);
+   evas_object_data_set(term->sendfile_request, "sendfile-request-timer", t);
+   elm_object_focus_set(term->sendfile_request, EINA_FALSE);
+   _term_focus(term);
+   return EINA_FALSE;
+}
+
+static void
+_sendfile_request_hide(Term *term)
+{
+   if (!term->sendfile_request_enabled) return;
+   if (term->sendfile_request_hide_timer)
+     ecore_timer_del(term->sendfile_request_hide_timer);
+   term->sendfile_request_hide_timer =
+     ecore_timer_add(0.2, _sendfile_request_hide_delay, term);
+}
+
+static void
+_sendfile_request_done(void *data, Evas_Object *obj EINA_UNUSED, void *info)
+{
+   Term *term = data;
+   const char *path, *selpath = info;
+
+   if (!term->sendfile_request) return;
+
+   path = elm_fileselector_path_get(term->sendfile_request);
+   eina_stringshare_replace(&term->sendfile_dir, path);
+
+   if (selpath)
+     {
+        _sendfile_progress(term);
+        termio_file_send_ok(term->termio, selpath);
+     }
+   else  termio_file_send_cancel(term->termio);
+   _sendfile_request_hide(term);
+}
+
+static void
+_sendfile_request(Term *term, const char *path)
+{
+   Evas_Object *o;
+   const char *p;
+
+   if (term->sendfile_request)
+     {
+        evas_object_del(term->sendfile_request);
+        term->sendfile_request = NULL;
+     }
+   if (!edje_object_part_exists(term->bg, "terminology.sendfile.request"))
+     {
+        termio_file_send_cancel(term->termio);
+        return;
+     }
+   if (term->sendfile_request_hide_timer)
+     {
+        ecore_timer_del(term->sendfile_request_hide_timer);
+        term->sendfile_request_hide_timer = NULL;
+     }
+   o = elm_fileselector_add(term->wn->win);
+   evas_object_data_set(o, "sendfile-request-term", term);
+   term->sendfile_request = o;
+   evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _sendfile_request_del, 
NULL);
+   elm_fileselector_is_save_set(o, EINA_TRUE);
+   elm_fileselector_expandable_set(o, EINA_FALSE);
+   if (!term->sendfile_dir)
+     {
+        const char *dir = eina_environment_home_get();
+
+        if (dir) term->sendfile_dir = eina_stringshare_add(dir);
+     }
+   if (term->sendfile_dir) elm_fileselector_path_set(o, term->sendfile_dir);
+   p = strrchr(path, '/');
+   if (p) elm_fileselector_current_name_set(o, p + 1);
+   else elm_fileselector_current_name_set(o, path);
+   evas_object_smart_callback_add(o, "done", _sendfile_request_done, term);
+   term->sendfile_request_enabled = EINA_TRUE;
+   edje_object_part_swallow(term->bg, "terminology.sendfile.request", o);
+   evas_object_show(o);
+   edje_object_signal_emit(term->bg, "sendfile,request,on", "terminology");
+   elm_object_focus_set(term->termio, EINA_FALSE);
+   elm_object_focus_set(o, EINA_TRUE);
+}
+
+static void
 _cb_command(void *data,
             Evas_Object *_obj EINA_UNUSED,
             void *event)
@@ -3749,7 +3999,7 @@ _cb_command(void *data,
           }
         else if (cmd[1] == 'q') // queue it to display after current one
           {
-              _popmedia_queue_add(term, cmd + 2);
+             _popmedia_queue_add(term, cmd + 2);
           }
      }
    else if (cmd[0] == 'b') // set background
@@ -3793,6 +4043,19 @@ _cb_command(void *data,
         else if (cmd[1] == 'p') // permanent
           _set_alpha(termio_config_get(term->termio), cmd + 2, EINA_TRUE);
      }
+   else if (cmd[0] == 'f') // file...
+     {
+        if (cmd[1] == 'r') // receive
+          {
+             _sendfile_request(term, cmd + 2);
+          }
+        else if (cmd[1] == 'd') // data packet
+          {
+          }
+        else if (cmd[1] == 'x') // exit data stream
+          {
+          }
+     }
 }
 
 static void
@@ -3872,6 +4135,28 @@ _cb_icon(void *data,
      elm_win_icon_name_set(term->wn->win, termio_icon_name_get(term->termio));
 }
 
+static void
+_cb_send_progress(void *data,
+                  Evas_Object *_obj EINA_UNUSED,
+                  void *_event EINA_UNUSED)
+{
+   Term *term = data;
+
+   elm_progressbar_value_set(term->sendfile_progress_bar,
+                             termio_file_send_progress_get(term->termio));
+}
+
+static void
+_cb_send_end(void *data,
+             Evas_Object *_obj EINA_UNUSED,
+             void *_event EINA_UNUSED)
+{
+   Term *term = data;
+   if (!term->sendfile_progress) return;
+   _sendfile_request_hide(term);
+   _sendfile_progress_hide(term);
+}
+
 static Eina_Bool
 _cb_cmd_focus(void *data)
 {
@@ -4227,6 +4512,31 @@ _term_free(Term *term)
 {
    const char *s;
 
+   if (term->sendfile_request)
+     {
+        evas_object_del(term->sendfile_request);
+        term->sendfile_request = NULL;
+     }
+   if (term->sendfile_progress)
+     {
+        evas_object_del(term->sendfile_progress);
+        term->sendfile_progress = NULL;
+     }
+   if (term->sendfile_request_hide_timer)
+     {
+        ecore_timer_del(term->sendfile_request_hide_timer);
+        term->sendfile_request_hide_timer = NULL;
+     }
+   if (term->sendfile_progress_hide_timer)
+     {
+        ecore_timer_del(term->sendfile_progress_hide_timer);
+        term->sendfile_progress_hide_timer = NULL;
+     }
+   if (term->sendfile_dir)
+     {
+        eina_stringshare_del(term->sendfile_dir);
+        term->sendfile_dir = NULL;
+     }
    EINA_LIST_FREE(term->popmedia_queue, s)
      {
         eina_stringshare_del(s);
@@ -4616,6 +4926,8 @@ term_new(Win *wn, Config *config, const char *cmd,
    evas_object_smart_callback_add(o, "split,v", _cb_split_v, term);
    evas_object_smart_callback_add(o, "title,change", _cb_title, term);
    evas_object_smart_callback_add(o, "icon,change", _cb_icon, term);
+   evas_object_smart_callback_add(o, "send,progress", _cb_send_progress, term);
+   evas_object_smart_callback_add(o, "send,end", _cb_send_end, term);
    evas_object_show(o);
 
    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN,

-- 


Reply via email to