Kernel/VTerm - Add 2DCmd push image support
[tpg/acess2.git] / KernelLand / Kernel / drv / vterm_2d.c
1 /*
2  * Acess2 Kernel
3  * - By John Hodge (thePowersGang)
4  *
5  * drv/vterm_2d.c
6  * - 2D command processing
7  */
8 #define DEBUG   0
9 #include "vterm.h"
10 #include <acess/devices/pty_cmds.h>
11
12 void    VT_int_SetCursorPos(tVTerm *Term, int X, int Y);
13 void    VT_int_SetCursorBitmap(tVTerm *Term, int W, int H);
14 size_t  VT_int_FillCursorBitmap(tVTerm *Term, size_t DataOfs, size_t Length, const void *Data);
15  int    VT_int_2DCmd_SetCursorPos(void *Handle, size_t Offset, size_t Length, const void *Data);
16  int    VT_int_2DCmd_SetCursorBitmap(void *Handle, size_t Offset, size_t Length, const void *Data);
17  int    VT_int_2DCmd_SendData(void *Handle, size_t Offset, size_t Length, const void *Data);
18
19 // === CODE ===
20 void VT_int_SetCursorPos(tVTerm *Term, int X, int Y)
21 {
22         if( Term->Mode == PTYBUFFMT_TEXT )
23         {
24                 tVT_Pos *wrpos = VT_int_GetWritePosPtr(Term);
25                 wrpos->Row = X;
26                 wrpos->Col = Y + (Term->Flags & VT_FLAG_ALTBUF ? 0 : Term->ViewTopRow);
27                 VT_int_UpdateCursor(Term, 0);
28         }
29         else
30         {
31                 Term->VideoCursorX = X;
32                 Term->VideoCursorY = Y;
33                 VT_int_UpdateCursor(Term, 1);
34         }
35 }
36
37 void VT_int_SetCursorBitmap(tVTerm *Term, int W, int H)
38 {
39         LOG("WxH = %ix%i", W, H);
40         if(Term->VideoCursor)
41         {
42                 if(W * H != Term->VideoCursor->W * Term->VideoCursor->H) {
43                         free(Term->VideoCursor);
44                         Term->VideoCursor = NULL;
45                 }
46         }
47         if(!Term->VideoCursor) {
48                 Term->VideoCursor = malloc(sizeof(*Term->VideoCursor) + W*H*sizeof(Uint32));
49                 if(!Term->VideoCursor) {
50                         Log_Error("VTerm", "Unable to allocate memory for cursor");
51                         return ;
52                 }
53         }
54         Term->VideoCursor->W = W;
55         Term->VideoCursor->H = H;
56         Term->VideoCursor->XOfs = 0;
57         Term->VideoCursor->YOfs = 0;
58 }
59
60 size_t VT_int_FillCursorBitmap(tVTerm *Term, size_t DataOfs, size_t Length, const void *Data)
61 {
62         if( !Term->VideoCursor )
63                 return 0;
64         size_t  limit = Term->VideoCursor->W * Term->VideoCursor->H * 4;
65         if( DataOfs > limit )
66                 return 0;
67         if( Length > limit )
68                 Length = limit;
69         if( DataOfs + Length > limit )
70                 Length = limit - DataOfs;
71         
72         LOG("memcpy(VideoCursor->Data + 0x%x, Data, 0x%x)", DataOfs, Length);
73         memcpy((char*)Term->VideoCursor->Data + DataOfs, Data, Length);
74         
75         if(gpVT_CurTerm == Term)
76                 VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, Term->VideoCursor);
77         return Length;
78 }
79
80 // --- 2D Command deserialising ---
81 int VT_int_2DCmd_SetCursorPos(void *Handle, size_t Offset, size_t Length, const void *Data)
82 {
83         struct ptycmd_setcursorpos      cmd;
84         if( Length < sizeof(cmd) )      return 0;
85         memcpy(&cmd, Data, sizeof(cmd));
86
87         VT_int_SetCursorPos(Handle, cmd.x, cmd.y);
88         
89         return sizeof(cmd);
90 }
91
92 int VT_int_2DCmd_SetCursorBitmap(void *Handle, size_t Offset, size_t Length, const void *Data)
93 {
94         struct ptycmd_setcursorbmp      cmd;
95         tVTerm  *term = Handle;
96         size_t  ret = 0;
97
98         if( Offset == 0 ) {
99                 if( Length < sizeof(cmd) )      return 0;
100                 memcpy(&cmd, Data, sizeof(cmd));
101                 VT_int_SetCursorBitmap(Handle, cmd.w, cmd.h);
102                 ret = sizeof(cmd);
103                 Length -= ret;
104                 Data = (const char*)Data + ret;
105                 Offset += ret;
106         }
107         
108         ASSERTC(Offset, >=, sizeof(cmd));
109
110         if( Length > 0 )
111         {
112                 ret += VT_int_FillCursorBitmap(Handle, Offset - sizeof(cmd), Length, Data);
113         }
114
115         LOG("%i + %i ==? %i", ret, Offset, term->Cmd2D.CurrentSize);
116         if( ret + Offset >= term->Cmd2D.CurrentSize )
117                 return ret;
118
119         return -ret;
120 }
121
122 int VT_int_2DCmd_SendData(void *Handle, size_t Offset, size_t Length, const void *Data)
123 {
124         tVTerm  *term = Handle;
125         struct ptycmd_senddata  cmd;
126         size_t  ret = 0;
127         
128         if( Offset == 0 )
129         {
130                 if( Length < sizeof(cmd) )
131                         return 0;
132                 memcpy(&cmd, Data, sizeof(cmd));
133                 
134                 ret = sizeof(cmd);
135                 Offset += ret;
136                 Length -= ret;
137                 Data = (const char*)Data + ret;
138                 
139                 term->Cmd2D.CmdInfo.Push.Offset = cmd.ofs*4;
140         }
141         
142         ASSERTC(Offset, >=, sizeof(cmd));
143         
144         if( Length > 0 )
145         {
146                 size_t  bytes = MIN(term->Width*term->Height*4 - term->Cmd2D.CmdInfo.Push.Offset, Length);
147                 LOG("bytes = %i (0x%x), Length = %i", bytes, bytes, Length);
148                 
149                 VT_int_PutFBData(term, term->Cmd2D.CmdInfo.Push.Offset, bytes, Data );
150                 term->Cmd2D.CmdInfo.Push.Offset += bytes;
151                 ret += bytes;
152                 
153                 LOG("bytes(%i) ==? 0 || ret(%i) + Offset(%i) ==? %i",
154                         bytes, ret, Offset, term->Cmd2D.CurrentSize);
155                 if( bytes == 0 || ret + Offset >= term->Cmd2D.CurrentSize )
156                         return ret;
157         }
158         
159         return -ret;
160 }
161
162 // > 0: Command complete
163 // = 0: Not enough data to start
164 // < 0: Ate -n bytes, still need more
165 typedef int     (*tVT_2DCmdHandler)(void *Handle, size_t Offset, size_t Length, const void *Data);
166 tVT_2DCmdHandler        gVT_2DCmdHandlers[] = {
167         [PTY2D_CMD_SETCURSORPOS] = VT_int_2DCmd_SetCursorPos,
168         [PTY2D_CMD_SETCURSORBMP] = VT_int_2DCmd_SetCursorBitmap,
169         [PTY2D_CMD_SEND] = VT_int_2DCmd_SendData,
170 };
171 const int       ciVT_Num2DCmdHandlers = sizeof(gVT_2DCmdHandlers)/sizeof(gVT_2DCmdHandlers[0]);
172
173 void VT_int_Handle2DCmd(void *Handle, size_t Length, const void *Data)
174 {
175         const uint8_t   *bdata = Data;
176         tVTerm  *term = Handle;
177
178         LOG("Length = 0x%x", Length);
179         // If a command didn't consume all the data it said it would, we have to clean up
180 _eat:
181         if( term->Cmd2D.PreEat )
182         {
183                 size_t  nom = MIN(term->Cmd2D.PreEat, Length);
184                 bdata += nom;
185                 Length -= nom;
186                 LOG("Discarding %i/%i ignored bytes of input", nom, term->Cmd2D.PreEat);
187                 term->Cmd2D.PreEat -= nom;
188         }
189
190         while( Length > 0 )
191         {
192                 const int cachesize = sizeof(term->Cmd2D.Cache);
193                 size_t  len, adjust;
194                 const void      *dataptr;
195         
196                 // Check for command resume
197                 if( term->Cmd2D.Current )
198                 {
199                         LOG("Resuming %i at +0x%x with 0x%x", term->Cmd2D.Current, term->Cmd2D.Offset, Length);
200                         dataptr = (void*)bdata;
201                         len = Length;
202                         adjust = 0;
203                 }
204                 // else begin a new command
205                 else
206                 {
207                         // If the new data would fit in the cache, or the cache is already populated
208                         // use the cache
209                         // - The cache should fit the header for every command, so all good
210                         if( Length < cachesize || term->Cmd2D.CachePos != 0 )
211                         {
212                                 adjust = term->Cmd2D.CachePos;
213                                 size_t newbytes = MIN(cachesize - term->Cmd2D.CachePos, Length);
214                                 memcpy(term->Cmd2D.Cache + term->Cmd2D.CachePos, bdata, newbytes);
215                                 term->Cmd2D.CachePos += newbytes;
216                                 dataptr = (void*)term->Cmd2D.Cache;
217                                 len = term->Cmd2D.CachePos;
218                         }
219                         else
220                         {
221                                 dataptr = (void*)bdata;
222                                 len = Length;
223                                 adjust = 0;
224                         }
225                         const struct ptycmd_header      *hdr = dataptr;
226
227                         // If there's not enough for the common header, wait for more
228                         if( len < sizeof(*hdr) )
229                         {
230                                 return ;
231                         }                       
232
233                         // Parse header
234                         term->Cmd2D.Offset = 0;
235                         term->Cmd2D.Current = hdr->cmd;
236                         term->Cmd2D.CurrentSize = (hdr->len_low | (hdr->len_hi << 8)) * 4;
237                         if( term->Cmd2D.CurrentSize == 0 ) {
238                                 Log_Warning("VTerm", "Command size too small (==0)");
239                                 term->Cmd2D.CurrentSize = 2;
240                         }
241                         LOG("Started %i with %s data",
242                                 term->Cmd2D.Current, (dataptr == bdata ? "direct" : "cache"));
243                 }
244
245                 // Sanity check
246                 if( term->Cmd2D.Current >= ciVT_Num2DCmdHandlers || !gVT_2DCmdHandlers[term->Cmd2D.Current] )
247                 {
248                         Log_Notice("VTerm", "2D Comand %i not handled", term->Cmd2D.Current);
249                         if( Length < term->Cmd2D.CurrentSize )
250                                 break;
251                         term->Cmd2D.CachePos = 0;
252                         term->Cmd2D.Current = 0;
253                         term->Cmd2D.PreEat = term->Cmd2D.CurrentSize;
254                         goto _eat;
255                 }
256                 
257                 const tVT_2DCmdHandler* handler = &gVT_2DCmdHandlers[term->Cmd2D.Current];
258                 #if 0
259                 if( term->Cmd2D.Offset == 0 )
260                 {
261                         if( len < handler->HeaderLength ) {
262                                 return ;
263                         }
264                         rv = handler->Header(Handle, len, dataptr);
265                 }
266                 else
267                 {
268                         rv = hander->Body(Handle, term->Cmd2D.Offset, len, dataptr);
269                 }
270                 #endif
271                 
272                 // Call Handler 
273                 int rv = (*handler)(Handle, term->Cmd2D.Offset, len, dataptr);
274                 LOG("2DCmd %i: rv=%i", term->Cmd2D.Current, rv);
275                 
276                 // If it returned 0 on the first call, it lacks space for the header
277                 if( rv == 0 && term->Cmd2D.Offset == 0 )
278                 {
279                         ASSERT( term->Cmd2D.CachePos != cachesize );
280                         // Clear current command because this command hasn't started yet
281                         term->Cmd2D.Current = 0;
282                         // Return, restart happens once all data is ready
283                         return ;
284                 }
285
286                 // Consume the byte count returned (adjust is the number of bytes that were already cached)
287                 size_t used_bytes = (rv < 0 ? -rv : rv) - adjust;
288                 Length -= used_bytes;
289                 bdata += used_bytes;
290                 term->Cmd2D.CachePos = 0;
291                 // If a negative count was returned, more data is expected
292                 if( rv < 0 ) {
293                         ASSERT( -rv <= len );
294                         LOG(" Incomplete");
295                         term->Cmd2D.Offset += -rv;
296                         continue ;
297                 }
298                 ASSERT(rv <= len);
299                 term->Cmd2D.Current = 0;
300
301                 // Eat up any uneaten data
302                 ASSERT( term->Cmd2D.Offset + rv <= term->Cmd2D.CurrentSize );
303                 if( term->Cmd2D.Offset + rv < term->Cmd2D.CurrentSize )
304                 {
305                         size_t  diff = term->Cmd2D.CurrentSize - (term->Cmd2D.Offset + rv);
306                         LOG("Left %i bytes", diff);
307                         term->Cmd2D.PreEat = diff;
308                         goto _eat;
309                 }
310                 LOG("Done (%i bytes left)", Length);
311         }
312 }

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