Kernel/drvutil - Fiddling with software cursor
[tpg/acess2.git] / Kernel / drvutil.c
1 /*
2  * Acess2 Kernel
3  * - By John Hodge
4  *
5  * drvutil.c
6  * - Common Driver/Filesystem Helper Functions
7  */
8 #define DEBUG   0
9 #include <acess.h>
10 #include <api_drv_disk.h>
11 #include <api_drv_video.h>
12
13 // === TYPES ===
14
15 // === PROTOTYPES ===
16 //int   DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers);
17 //size_t        DrvUtil_Video_WriteLFB(int Mode, tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Src);
18 //void  DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap);
19 //void  DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y);
20 void    DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf);
21 //void  DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf);
22 void    DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);
23 void    DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);
24
25 // === GLOBALS ===
26 tDrvUtil_Video_2DHandlers       gDrvUtil_Stub_2DFunctions = {
27         NULL,
28         DrvUtil_Video_2D_Fill,
29         DrvUtil_Video_2D_Blit
30 };
31
32 // === CODE ===
33 // --- Video Driver Helpers ---
34 int DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length,
35         tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers)
36 {
37         void    *stream = Buffer;
38          int    rem = Length;
39          int    op;
40         while( rem )
41         {
42                 rem --;
43                 op = *(Uint8*)stream;
44                 stream = (void*)((tVAddr)stream + 1);
45                 
46                 if(op > NUM_VIDEO_2DOPS) {
47                         Log_Warning("DrvUtil",
48                                 "DrvUtil_Video_2DStream: Unknown operation %i",
49                                 op);
50                         return Length-rem;
51                 }
52                 
53                 if(op*sizeof(void*) > SizeofHandlers) {
54                         Log_Warning("DrvUtil",
55                                 "DrvUtil_Video_2DStream: Driver does not support op %i",
56                                 op);
57                         return Length-rem;
58                 }
59                 
60                 switch(op)
61                 {
62                 case VIDEO_2DOP_NOP:    break;
63                 
64                 case VIDEO_2DOP_FILL:
65                         if(rem < 12)    return Length-rem;
66                         
67                         if(!Handlers->Fill) {
68                                 Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver"
69                                         " does not support VIDEO_2DOP_FILL");
70                                 return Length-rem;
71                         }
72                         
73                         Handlers->Fill(
74                                 Ent,
75                                 ((Uint16*)stream)[0], ((Uint16*)stream)[1],
76                                 ((Uint16*)stream)[2], ((Uint16*)stream)[3],
77                                 ((Uint32*)stream)[2]
78                                 );
79                         
80                         rem -= 12;
81                         stream = (void*)((tVAddr)stream + 12);
82                         break;
83                 
84                 case VIDEO_2DOP_BLIT:
85                         if(rem < 12)    return Length-rem;
86                         
87                         if(!Handlers->Blit) {
88                                 Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver"
89                                         " does not support VIDEO_2DOP_BLIT");
90                                 return Length-rem;
91                         }
92                         
93                         Handlers->Blit(
94                                 Ent,
95                                 ((Uint16*)stream)[0], ((Uint16*)stream)[1],
96                                 ((Uint16*)stream)[2], ((Uint16*)stream)[3],
97                                 ((Uint16*)stream)[4], ((Uint16*)stream)[5]
98                                 );
99                         
100                         rem -= 12;
101                         stream = (void*)((tVAddr)stream + 12);
102                         break;
103                 
104                 }
105         }
106         return 0;
107 }
108
109 int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Buffer)
110 {
111         Uint8   *dest;
112         ENTER("pFBInfo xOffset xLength pBuffer",
113                 Mode, FBInfo, Offset, Length, Buffer);
114         switch( FBInfo->BufferFormat )
115         {
116         case VIDEO_BUFFMT_TEXT:
117                 {
118                 tVT_Char        *chars = Buffer;
119                  int    bytes_per_px = (FBInfo->Depth + 7) / 8;
120                  int    widthInChars = FBInfo->Width/giVT_CharWidth;
121                  int    heightInChars = FBInfo->Height/giVT_CharHeight;
122                  int    x, y, i;
123         
124                 LOG("bytes_per_px = %i", bytes_per_px);
125                 LOG("widthInChars = %i, heightInChars = %i", widthInChars, heightInChars);
126         
127                 Length /= sizeof(tVT_Char);     Offset /= sizeof(tVT_Char);
128                 
129                 x = Offset % widthInChars;      y = Offset / widthInChars;
130                 LOG("x = %i, y = %i", x, y);    
131         
132                 // Sanity Check
133                 if(Offset > heightInChars * widthInChars)       LEAVE_RET('i', 0);
134                 if(y >= heightInChars)  LEAVE_RET('i', 0);
135                 
136                 if( Offset + Length > heightInChars*widthInChars )
137                 {
138                         Length = heightInChars*widthInChars - Offset;
139                 }
140                 
141                 dest = FBInfo->Framebuffer;
142                 LOG("dest = %p", dest);
143                 dest += y * giVT_CharHeight * FBInfo->Pitch;
144                 LOG("dest = %p", dest);
145                 
146                 for( i = 0; i < Length; i++ )
147                 {
148                         if( y >= heightInChars )
149                         {
150                                 Log_Notice("DrvUtil", "Stopped at %i", i);
151                                 break;
152                         }
153
154                         VT_Font_Render(
155                                 chars->Ch,
156                                 dest + x*giVT_CharWidth*bytes_per_px, FBInfo->Depth, FBInfo->Pitch,
157                                 VT_Colour12toN(chars->BGCol, FBInfo->Depth),
158                                 VT_Colour12toN(chars->FGCol, FBInfo->Depth)
159                                 );
160                         
161                         chars ++;
162                         x ++;
163                         if( x >= widthInChars )
164                         {
165                                 x = 0;
166                                 y ++;
167                                 dest += FBInfo->Pitch*giVT_CharHeight;
168                                 LOG("dest = %p", dest);
169                         }
170                 }
171                 Length = i * sizeof(tVT_Char);
172                 }
173                 break;
174         
175         case VIDEO_BUFFMT_FRAMEBUFFER:
176                 if(FBInfo->Width*FBInfo->Height*4 < Offset+Length)
177                 {
178                         Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Framebuffer Overflow");
179                         return 0;
180                 }
181                 
182                 //TODO: Handle non 32-bpp framebuffer modes
183                 if( FBInfo->Depth != 32 ) {
184                         Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Don't support non 32-bpp FB mode");
185                         return 0;
186                 }       
187
188                 
189                 //TODO: Handle pitch != Width*BytesPerPixel
190                 // Copy to Frambuffer
191                 dest = (Uint8 *)FBInfo->Framebuffer + Offset;
192                 memcpy(dest, Buffer, Length);
193                 break;
194         
195         case VIDEO_BUFFMT_2DSTREAM:
196                 Length = DrvUtil_Video_2DStream(
197                         FBInfo, Buffer, Length,
198                         &gDrvUtil_Stub_2DFunctions, sizeof(gDrvUtil_Stub_2DFunctions)
199                         );
200                 break;
201         
202         default:
203                 LEAVE('i', -1);
204                 return -1;
205         }
206         LEAVE('x', Length);
207         return Length;
208 }
209
210 void DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap)
211 {
212          int    csrX = Buf->CursorX, csrY = Buf->CursorY;
213         size_t  size;
214
215         // Clear old bitmap
216         if( Buf->CursorBitmap )
217         {
218                 DrvUtil_Video_RemoveCursor(Buf);
219                 if( !Bitmap || Bitmap->W != Buf->CursorBitmap->W || Bitmap->H != Buf->CursorBitmap->H )
220                 {
221                         free( Buf->CursorSaveBuf );
222                         Buf->CursorSaveBuf = NULL;
223                 }
224                 free(Buf->CursorBitmap);
225                 Buf->CursorBitmap = NULL;
226         }
227         
228         // If the new bitmap is null, disable drawing
229         if( !Bitmap )
230         {
231                 Buf->CursorX = -1;
232                 Buf->CursorY = -1;
233                 return ;
234         }
235
236         // Check the new bitmap is valid
237         size = sizeof(tVideo_IOCtl_Bitmap) + Bitmap->W*Bitmap->H*4;
238         if( !CheckMem(Bitmap, size) ) {
239                 Log_Warning("DrvUtil", "DrvUtil_Video_SetCursor: Bitmap (%p) is in invalid memory", Bitmap);
240                 return;
241         }
242         
243         // Take a copy
244         Buf->CursorBitmap = malloc( size );
245         memcpy(Buf->CursorBitmap, Bitmap, size);
246         
247         // Restore cursor position
248         DrvUtil_Video_DrawCursor(Buf, csrX, csrY);
249 }
250
251 void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y)
252 {
253          int    render_ox=0, render_oy=0, render_w, render_h;
254
255         // X < 0 disables the cursor
256         if( X < 0 ) {
257                 Render->CursorX = -1;
258                 return ;
259         }
260
261         // Sanity checking
262         if( X < 0 || Y < 0 )    return;
263         if( X >= Buf->Width || Y >= Buf->Height )       return;
264
265         // Ensure the cursor is enabled
266         if( !Buf->CursorBitmap )        return ;
267         
268         // Save cursor position (for changing the bitmap)
269         Buf->CursorX = X;       Buf->CursorY = Y;
270
271         // Apply cursor's center offset
272         X -= Buf->CursorBitmap->XOfs;
273         Y -= Buf->CursorBitmap->YOfs;
274         
275         // Get the width of the cursor on screen (clipping to right/bottom edges)
276         render_w = X > Buf->Width  - Buf->CursorBitmap->W ? Buf->Width  - X : Buf->CursorBitmap->W;
277         render_h = Y > Buf->Height - Buf->CursorBitmap->H ? Buf->Height - Y : Buf->CursorBitmap->H;
278
279         // Clipp to left/top edges
280         if(X < 0) {     render_ox = -X; X = 0;  }
281         if(Y < 0) {     render_oy = -Y; Y = 0;  }
282
283         // Save values
284         Buf->CursorRenderW = render_w;  Buf->CursorRenderH = render_h;
285         Buf->CursorDestX   = X;         Buf->CursorDestY = Y;
286         Buf->CursorReadX   = render_ox; Buf->CursorReadY = render_oy;
287
288         // Call render routine
289         DrvUtil_Video_RenderCursor(Buf);
290 }
291
292 void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf)
293 {
294          int    src_x = Buf->CursorReadX, src_y = Buf->CursorReadY;
295          int    render_w = Buf->CursorRenderW, render_h = Buf->CursorRenderH;
296          int    dest_x = Buf->CursorDestX, dest_y = Buf->CursorDestY;
297          int    bytes_per_px = (Buf->Depth + 7) / 8;
298          int    save_pitch = Buf->CursorBitmap->W * bytes_per_px;
299         void    *dest;
300         Uint32  *src;
301          int    x, y;
302
303         dest = (Uint8*)Buf->Framebuffer + dest_y*Buf->Pitch + dest_x*bytes_per_px;
304         src = Buf->CursorBitmap->Data + src_y * Buf->CursorBitmap->W + src_x;
305         
306         // Allocate save buffer if not already
307         if( !Buf->CursorSaveBuf )
308                 Buf->CursorSaveBuf = malloc( Buf->CursorBitmap->W*Buf->CursorBitmap->H*bytes_per_px );
309
310         // Save behind the cursor
311         for( y = 0; y < render_h; y ++ )
312                 memcpy(
313                         (Uint8*)Buf->CursorSaveBuf + y*save_pitch,
314                         (Uint8*)dest + y*Buf->Pitch,
315                         render_w*bytes_per_px
316                         );
317         // Draw the cursor
318         switch(Buf->Depth)
319         {
320         case 32:
321                 for( y = 0; y < render_h; y ++ )
322                 {
323                         for(x = 0; x < render_w; x ++ )
324                         {
325                                 // TODO: Should I implement alpha blending?
326                                 if(src[x] & 0xFF000000)
327                                         ((Uint32*)dest)[x] = src[x];
328                                 else
329                                         ;       // NOP, completely transparent
330                         }
331                         src += Buf->CursorBitmap->W;
332                         dest = (Uint8*)dest + Buf->Pitch;
333                 }
334                 break;
335         default:
336                 Log_Error("DrvUtil", "TODO: Implement non 32-bpp cursor");
337                 break;
338         }
339 }
340
341 void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf)
342 {
343          int    bytes_per_px = (Buf->Depth + 7) / 8;
344          int    y, save_pitch;
345         Uint8   *dest, *src;
346
347         // Just a little sanity
348         if( !Buf->CursorBitmap || Buf->CursorX == -1 )  return ;
349         if( !Buf->CursorSaveBuf )       return ;
350
351         // Set up
352         save_pitch = Buf->CursorBitmap->W * bytes_per_px;
353         dest = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px;
354         src = Buf->CursorSaveBuf;
355         
356         // Copy each line back
357         for( y = 0; y < Buf->CursorRenderH; y ++ )
358         {
359                 memcpy( dest, src, Buf->CursorRenderW * bytes_per_px );
360                 src += save_pitch;
361                 dest += Buf->Pitch;
362         }
363         
364         // Set the cursor as removed
365         Buf->CursorX = -1;
366 }
367
368 void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour)
369 {
370         tDrvUtil_Video_BufInfo  *FBInfo = Ent;
371
372         // TODO: Handle non-32bit modes
373         if( FBInfo->Depth != 32 )       return;
374
375         // TODO: Be less hacky
376          int    pitch = FBInfo->Pitch/4;
377         Uint32  *buf = (Uint32*)FBInfo->Framebuffer + Y*pitch + X;
378         while( H -- ) {
379                 Uint32 *tmp;
380                  int    i;
381                 tmp = buf;
382                 for(i=W;i--;tmp++)      *tmp = Colour;
383                 buf += pitch;
384         }
385 }
386
387 void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H)
388 {
389         tDrvUtil_Video_BufInfo  *FBInfo = Ent;
390          int    scrnpitch = FBInfo->Pitch;
391          int    bytes_per_px = (FBInfo->Depth + 7) / 8;
392          int    dst = DstY*scrnpitch + DstX;
393          int    src = SrcY*scrnpitch + SrcX;
394          int    tmp;
395         
396         //Log("Vesa_2D_Blit: (Ent=%p, DstX=%i, DstY=%i, SrcX=%i, SrcY=%i, W=%i, H=%i)",
397         //      Ent, DstX, DstY, SrcX, SrcY, W, H);
398         
399         if(SrcX + W > FBInfo->Width)    W = FBInfo->Width - SrcX;
400         if(DstX + W > FBInfo->Width)    W = FBInfo->Width - DstX;
401         if(SrcY + H > FBInfo->Height)   H = FBInfo->Height - SrcY;
402         if(DstY + H > FBInfo->Height)   H = FBInfo->Height - DstY;
403         
404         //Debug("W = %i, H = %i", W, H);
405         
406         if( dst > src ) {
407                 // Reverse copy
408                 dst += H*scrnpitch;
409                 src += H*scrnpitch;
410                 while( H -- ) {
411                         dst -= scrnpitch;
412                         src -= scrnpitch;
413                         tmp = W*bytes_per_px;
414                         for( tmp = W; tmp --; ) {
415                                 *((Uint8*)FBInfo->Framebuffer + dst + tmp) = *((Uint8*)FBInfo->Framebuffer + src + tmp);
416                         }
417                 }
418         }
419         else {
420                 // Normal copy is OK
421                 while( H -- ) {
422                         memcpy((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, W*bytes_per_px);
423                         dst += scrnpitch;
424                         src += scrnpitch;
425                 }
426         }
427         //Log("Vesa_2D_Blit: RETURN");
428 }
429         
430
431 // --- Disk Driver Helpers ---
432 Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer,
433         tDrvUtil_Callback ReadBlocks, Uint64 BlockSize, Uint Argument)
434 {
435         Uint8   tmp[BlockSize]; // C99
436         Uint64  block = Start / BlockSize;
437          int    offset = Start - block * BlockSize;
438          int    leading = BlockSize - offset;
439         Uint64  num;
440          int    tailings;
441         Uint64  ret;
442         
443         ENTER("XStart XLength pBuffer pReadBlocks XBlockSize xArgument",
444                 Start, Length, Buffer, ReadBlocks, BlockSize, Argument);
445         
446         // Non aligned start, let's fix that!
447         if(offset != 0)
448         {
449                 if(leading > Length)    leading = Length;
450                 LOG("Reading %i bytes from Block1+%i", leading, offset);
451                 ret = ReadBlocks(block, 1, tmp, Argument);
452                 if(ret != 1) {
453                         LEAVE('i', 0);
454                         return 0;
455                 }
456                 memcpy( Buffer, &tmp[offset], leading );
457                 
458                 if(leading == Length) {
459                         LEAVE('i', leading);
460                         return leading;
461                 }
462                 
463                 Buffer = (Uint8*)Buffer + leading;
464                 block ++;
465                 num = ( Length - leading ) / BlockSize;
466                 tailings = Length - num * BlockSize - leading;
467         }
468         else {
469                 num = Length / BlockSize;
470                 tailings = Length % BlockSize;
471         }
472         
473         // Read central blocks
474         if(num)
475         {
476                 LOG("Reading %i blocks", num);
477                 ret = ReadBlocks(block, num, Buffer, Argument);
478                 if(ret != num ) {
479                         LEAVE('X', leading + ret * BlockSize);
480                         return leading + ret * BlockSize;
481                 }
482         }
483         
484         // Read last tailing block
485         if(tailings != 0)
486         {
487                 LOG("Reading %i bytes from last block", tailings);
488                 block += num;
489                 Buffer = (Uint8*)Buffer + num * BlockSize;
490                 ret = ReadBlocks(block, 1, tmp, Argument);
491                 if(ret != 1) {
492                         LEAVE('X', leading + num * BlockSize);
493                         return leading + num * BlockSize;
494                 }
495                 memcpy( Buffer, tmp, tailings );
496         }
497         
498         LEAVE('X', Length);
499         return Length;
500 }
501
502 Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, void *Buffer,
503         tDrvUtil_Callback ReadBlocks, tDrvUtil_Callback WriteBlocks,
504         Uint64 BlockSize, Uint Argument)
505 {
506         Uint8   tmp[BlockSize]; // C99
507         Uint64  block = Start / BlockSize;
508          int    offset = Start - block * BlockSize;
509          int    leading = BlockSize - offset;
510         Uint64  num;
511          int    tailings;
512         Uint64  ret;
513         
514         ENTER("XStart XLength pBuffer pReadBlocks pWriteBlocks XBlockSize xArgument",
515                 Start, Length, Buffer, ReadBlocks, WriteBlocks, BlockSize, Argument);
516         
517         // Non aligned start, let's fix that!
518         if(offset != 0)
519         {
520                 if(leading > Length)    leading = Length;
521                 LOG("Writing %i bytes to Block1+%i", leading, offset);
522                 // Read a copy of the block
523                 ret = ReadBlocks(block, 1, tmp, Argument);
524                 if(ret != 1) {
525                         LEAVE('i', 0);
526                         return 0;
527                 }
528                 // Modify
529                 memcpy( &tmp[offset], Buffer, leading );
530                 // Write Back
531                 ret = WriteBlocks(block, 1, tmp, Argument);
532                 if(ret != 1) {
533                         LEAVE('i', 0);
534                         return 0;
535                 }
536                 
537                 if(leading == Length) {
538                         LEAVE('i', leading);
539                         return leading;
540                 }
541                 
542                 Buffer = (Uint8*)Buffer + leading;
543                 block ++;
544                 num = ( Length - leading ) / BlockSize;
545                 tailings = Length - num * BlockSize - leading;
546         }
547         else {
548                 num = Length / BlockSize;
549                 tailings = Length % BlockSize;
550         }
551         
552         // Read central blocks
553         if(num)
554         {
555                 LOG("Writing %i blocks", num);
556                 ret = WriteBlocks(block, num, Buffer, Argument);
557                 if(ret != num ) {
558                         LEAVE('X', leading + ret * BlockSize);
559                         return leading + ret * BlockSize;
560                 }
561         }
562         
563         // Read last tailing block
564         if(tailings != 0)
565         {
566                 LOG("Writing %i bytes to last block", tailings);
567                 block += num;
568                 Buffer = (Uint8*)Buffer + num * BlockSize;
569                 // Read
570                 ret = ReadBlocks(block, 1, tmp, Argument);
571                 if(ret != 1) {
572                         LEAVE('X', leading + num * BlockSize);
573                         return leading + num * BlockSize;
574                 }
575                 // Modify
576                 memcpy( tmp, Buffer, tailings );
577                 // Write
578                 ret = WriteBlocks(block, 1, tmp, Argument);
579                 if(ret != 1) {
580                         LEAVE('X', leading + num * BlockSize);
581                         return leading + num * BlockSize;
582                 }
583                 
584         }
585         
586         LEAVE('X', Length);
587         return Length;
588 }

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