Kernel/VTerm - Include forgotten 2D command handling
[tpg/acess2.git] / KernelLand / Kernel / drv / vterm_2d.c
diff --git a/KernelLand/Kernel/drv/vterm_2d.c b/KernelLand/Kernel/drv/vterm_2d.c
new file mode 100644 (file)
index 0000000..aadf7a2
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * drv/vterm_2d.c
+ * - 2D command processing
+ */
+#define DEBUG  0
+#include "vterm.h"
+#include <acess/devices/pty_cmds.h>
+
+void   VT_int_SetCursorPos(tVTerm *Term, int X, int Y);
+void   VT_int_SetCursorBitmap(tVTerm *Term, int W, int H);
+size_t VT_int_FillCursorBitmap(tVTerm *Term, size_t DataOfs, size_t Length, const void *Data);
+ int   VT_int_2DCmd_SetCursorPos(void *Handle, size_t Offset, size_t Length, const void *Data);
+ int   VT_int_2DCmd_SetCursorBitmap(void *Handle, size_t Offset, size_t Length, const void *Data);
+
+// === CODE ===
+void VT_int_SetCursorPos(tVTerm *Term, int X, int Y)
+{
+       if( Term->Mode == PTYBUFFMT_TEXT )
+       {
+               if(Term->Flags & VT_FLAG_ALTBUF)
+                       Term->AltWritePos = X + Y * Term->TextWidth;
+               else
+                       Term->WritePos = X + Y * Term->TextWidth + Term->ViewPos;
+               VT_int_UpdateCursor(Term, 0);
+       }
+       else
+       {
+               Term->VideoCursorX = X;
+               Term->VideoCursorY = Y;
+               VT_int_UpdateCursor(Term, 1);
+       }
+}
+
+void VT_int_SetCursorBitmap(tVTerm *Term, int W, int H)
+{
+       LOG("WxH = %ix%i", W, H);
+       if(Term->VideoCursor)
+       {
+               if(W * H != Term->VideoCursor->W * Term->VideoCursor->H) {
+                       free(Term->VideoCursor);
+                       Term->VideoCursor = NULL;
+               }
+       }
+       if(!Term->VideoCursor) {
+               Term->VideoCursor = malloc(sizeof(*Term->VideoCursor) + W*H*sizeof(Uint32));
+               if(!Term->VideoCursor) {
+                       Log_Error("VTerm", "Unable to allocate memory for cursor");
+                       return ;
+               }
+       }
+       Term->VideoCursor->W = W;
+       Term->VideoCursor->H = H;
+}
+
+size_t VT_int_FillCursorBitmap(tVTerm *Term, size_t DataOfs, size_t Length, const void *Data)
+{
+       if( !Term->VideoCursor )
+               return 0;
+       size_t  limit = Term->VideoCursor->W * Term->VideoCursor->H * 4;
+       if( DataOfs > limit )
+               return 0;
+       if( Length > limit )
+               Length = limit;
+       if( DataOfs + Length > limit )
+               Length = limit - DataOfs;
+       
+       LOG("memcpy(VideoCursor->Data + 0x%x, Data, 0x%x)", DataOfs, Length);
+       memcpy((char*)Term->VideoCursor->Data + DataOfs, Data, Length);
+       
+       if(gpVT_CurTerm == Term)
+               VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, Term->VideoCursor);
+       return Length;
+}
+
+// --- 2D Command deserialising ---
+int VT_int_2DCmd_SetCursorPos(void *Handle, size_t Offset, size_t Length, const void *Data)
+{
+       struct ptycmd_setcursorpos      cmd;
+       if( Length < sizeof(cmd) )      return 0;
+       memcpy(&cmd, Data, sizeof(cmd));
+
+       VT_int_SetCursorPos(Handle, cmd.x, cmd.y);
+       
+       return sizeof(cmd);
+}
+
+int VT_int_2DCmd_SetCursorBitmap(void *Handle, size_t Offset, size_t Length, const void *Data)
+{
+       struct ptycmd_setcursorbmp      cmd;
+       tVTerm  *term = Handle;
+       size_t  ret = 0;
+
+       if( Offset == 0 ) {
+               if( Length < sizeof(cmd) )      return 0;
+               memcpy(&cmd, Data, sizeof(cmd));
+               VT_int_SetCursorBitmap(Handle, cmd.w, cmd.h);
+               ret = sizeof(cmd);
+               Length -= ret;
+               Data = (const char*)Data + ret;
+               Offset += ret;
+               if( Length == 0 )
+                       return -ret;
+       }
+
+
+       if( Offset < sizeof(cmd) ) {
+               // oops?
+               return ret;
+       }
+
+       ret += VT_int_FillCursorBitmap(Handle, Offset - sizeof(cmd), Length, Data);
+
+       LOG("%i + %i ==? %i", ret, Offset, term->Cmd2D.CurrentSize);
+       if( ret + Offset >= term->Cmd2D.CurrentSize )
+               return ret;
+
+       return -ret;
+}
+
+// > 0: Command complete
+// = 0: Not enough data to start
+// < 0: Ate -n bytes, still need more
+typedef int    (*tVT_2DCmdHandler)(void *Handle, size_t Offset, size_t Length, const void *Data);
+tVT_2DCmdHandler       gVT_2DCmdHandlers[] = {
+       [PTY2D_CMD_SETCURSORPOS] = VT_int_2DCmd_SetCursorPos,
+       [PTY2D_CMD_SETCURSORBMP] = VT_int_2DCmd_SetCursorBitmap,
+};
+const int      ciVT_Num2DCmdHandlers = sizeof(gVT_2DCmdHandlers)/sizeof(gVT_2DCmdHandlers[0]);
+
+void VT_int_Handle2DCmd(void *Handle, size_t Length, const void *Data)
+{
+       const uint8_t   *bdata = Data;
+       tVTerm  *term = Handle;
+
+       LOG("Length = 0x%x", Length);
+       // If a command terminated early, we have to clean up its data
+_eat:
+       if( term->Cmd2D.PreEat )
+       {
+               size_t  nom = MIN(term->Cmd2D.PreEat, Length);
+               bdata += nom;
+               Length -= nom;
+               LOG("Discarding %i/%i ignored bytes of input", nom, term->Cmd2D.PreEat);
+               term->Cmd2D.PreEat -= nom;
+       }
+
+       while( Length > 0 )
+       {
+               const int cachesize = sizeof(term->Cmd2D.Cache);
+               size_t  len, adjust;
+               const void      *dataptr;
+       
+               // Check for command resume
+               if( term->Cmd2D.Current )
+               {
+                       LOG("Resuming %i at +0x%x with 0x%x", term->Cmd2D.Current, term->Cmd2D.Offset, Length);
+                       dataptr = (void*)bdata;
+                       len = Length;
+                       adjust = 0;
+               }
+               // else begin a new command
+               else
+               {               
+                       if( Length < cachesize || term->Cmd2D.CachePos != 0 )
+                       {
+                               adjust = term->Cmd2D.CachePos;
+                               size_t newbytes = MIN(cachesize - term->Cmd2D.CachePos, Length);
+                               memcpy(term->Cmd2D.Cache + term->Cmd2D.CachePos, bdata, newbytes);
+                               term->Cmd2D.CachePos += newbytes;
+                               dataptr = (void*)term->Cmd2D.Cache;
+                               len = term->Cmd2D.CachePos;
+                       }
+                       else {
+                               dataptr = (void*)bdata;
+                               len = Length;
+                               adjust = 0;
+                       }
+                       const struct ptycmd_header      *hdr = dataptr;
+
+                       if( len < sizeof(*hdr) ) {
+                               return ;
+                       }                       
+
+                       term->Cmd2D.Offset = 0;
+                       term->Cmd2D.Current = hdr->cmd;
+                       term->Cmd2D.CurrentSize = (hdr->len_low | (hdr->len_hi << 8)) * 4;
+                       if( term->Cmd2D.CurrentSize == 0 )
+                               term->Cmd2D.CurrentSize = 2;
+                       LOG("Started %i with %s data",
+                               term->Cmd2D.Current, (dataptr == bdata ? "direct" : "cache"));
+               }
+               if( term->Cmd2D.Current >= ciVT_Num2DCmdHandlers || !gVT_2DCmdHandlers[term->Cmd2D.Current] )
+               {
+                       Log_Notice("VTerm", "2D Comand %i not handled", term->Cmd2D.Current);
+                       if( Length < term->Cmd2D.CurrentSize )
+                               break;
+                       term->Cmd2D.CachePos = 0;
+                       term->Cmd2D.Current = 0;
+                       term->Cmd2D.PreEat = term->Cmd2D.CurrentSize;
+                       goto _eat;
+               }
+               else
+               {
+                       int rv = gVT_2DCmdHandlers[term->Cmd2D.Current](Handle, term->Cmd2D.Offset, len, dataptr);
+                       LOG("2DCmd %i: rv=%i", term->Cmd2D.Current, rv);
+                       if( rv == 0 && term->Cmd2D.Offset == 0 ) {
+                               // 0: Not enough data for header
+                               ASSERT( term->Cmd2D.CachePos != cachesize );
+                               // Clear current command because this command hasn't started yet
+                               term->Cmd2D.Current = 0;
+                               // Return, restart happens once all data is ready
+                               return ;
+                       }
+                       size_t used_bytes = (rv < 0 ? -rv : rv) - adjust;
+                       Length -= used_bytes;
+                       bdata += used_bytes;
+                       term->Cmd2D.CachePos = 0;
+                       if( rv < 0 ) {
+                               ASSERT( -rv <= len );
+                               LOG(" Incomplete");
+                               term->Cmd2D.Offset += -rv;
+                               continue ;
+                       }
+                       ASSERT(rv <= len);
+                       term->Cmd2D.Current = 0;
+
+                       // Eat up any uneaten data
+                       // - TODO: Need to eat across writes
+                       ASSERT( term->Cmd2D.Offset + rv <= term->Cmd2D.CurrentSize );
+                       if( term->Cmd2D.Offset + rv < term->Cmd2D.CurrentSize )
+                       {
+                               size_t  diff = term->Cmd2D.CurrentSize - (term->Cmd2D.Offset + rv);
+                               LOG("Left %i bytes", diff);
+                               term->Cmd2D.PreEat = diff;
+                               goto _eat;
+                       }
+                       LOG("Done (%i bytes left)", Length);
+               }
+       }
+}

UCC git Repository :: git.ucc.asn.au