+int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap)
+{
+ int csrX = Buf->CursorX, csrY = Buf->CursorY;
+ size_t size;
+
+ // Clear old bitmap
+ if( Buf->CursorBitmap )
+ {
+ DrvUtil_Video_RemoveCursor(Buf);
+ if( !Bitmap || Bitmap->W != Buf->CursorBitmap->W || Bitmap->H != Buf->CursorBitmap->H )
+ {
+ free( Buf->CursorSaveBuf );
+ Buf->CursorSaveBuf = NULL;
+ }
+ if( Buf->CursorBitmap != &gDrvUtil_TextModeCursor)
+ free(Buf->CursorBitmap);
+ Buf->CursorBitmap = NULL;
+ }
+
+ // If the new bitmap is null, disable drawing
+ if( !Bitmap )
+ {
+ Buf->CursorX = -1;
+ Buf->CursorY = -1;
+ return 0;
+ }
+
+ // Sanity check the bitmap
+ if( !CheckMem(Bitmap, sizeof(*Bitmap)) || !CheckMem(Bitmap->Data, Bitmap->W*Bitmap->H*sizeof(Uint32)) )
+ {
+ Log_Warning("DrvUtil", "DrvUtil_Video_SetCursor: Bitmap (%p) is in invalid memory", Bitmap);
+ errno = -EINVAL;
+ return -1;
+ }
+
+ // Don't take a copy of the DrvUtil provided cursor
+ if( Bitmap == &gDrvUtil_TextModeCursor )
+ {
+ Buf->CursorBitmap = Bitmap;
+ }
+ else
+ {
+ size = sizeof(tVideo_IOCtl_Bitmap) + Bitmap->W*Bitmap->H*4;
+
+ // Take a copy
+ Buf->CursorBitmap = malloc( size );
+ memcpy(Buf->CursorBitmap, Bitmap, size);
+ }
+
+ // Restore cursor position
+ DrvUtil_Video_DrawCursor(Buf, csrX, csrY);
+ return 0;
+}
+
+void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y)
+{
+ int render_ox=0, render_oy=0, render_w, render_h;
+
+ DrvUtil_Video_RemoveCursor(Buf);
+
+ // X < 0 disables the cursor
+ if( X < 0 ) {
+ Buf->CursorX = -1;
+ return ;
+ }
+
+ // Sanity checking
+ if( X < 0 || Y < 0 ) return;
+ if( X >= Buf->Width || Y >= Buf->Height ) return;
+
+ // Ensure the cursor is enabled
+ if( !Buf->CursorBitmap ) return ;
+
+ // Save cursor position (for changing the bitmap)
+ Buf->CursorX = X; Buf->CursorY = Y;
+
+ // Apply cursor's center offset
+ X -= Buf->CursorBitmap->XOfs;
+ Y -= Buf->CursorBitmap->YOfs;
+
+ // Get the width of the cursor on screen (clipping to right/bottom edges)
+ render_w = X > Buf->Width - Buf->CursorBitmap->W ? Buf->Width - X : Buf->CursorBitmap->W;
+ render_h = Y > Buf->Height - Buf->CursorBitmap->H ? Buf->Height - Y : Buf->CursorBitmap->H;
+
+ // Clipp to left/top edges
+ if(X < 0) { render_ox = -X; X = 0; }
+ if(Y < 0) { render_oy = -Y; Y = 0; }
+
+ // Save values
+ Buf->CursorRenderW = render_w; Buf->CursorRenderH = render_h;
+ Buf->CursorDestX = X; Buf->CursorDestY = Y;
+ Buf->CursorReadX = render_ox; Buf->CursorReadY = render_oy;
+
+ // Call render routine
+ DrvUtil_Video_RenderCursor(Buf);
+}
+
+void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf)
+{
+ int src_x = Buf->CursorReadX, src_y = Buf->CursorReadY;
+ int render_w = Buf->CursorRenderW, render_h = Buf->CursorRenderH;
+ int dest_x = Buf->CursorDestX, dest_y = Buf->CursorDestY;
+ int bytes_per_px = (Buf->Depth + 7) / 8;
+ int save_pitch = Buf->CursorBitmap->W * bytes_per_px;
+ void *dest;
+ Uint32 *src;
+ int x, y;
+
+ dest = (Uint8*)Buf->Framebuffer + dest_y*Buf->Pitch + dest_x*bytes_per_px;
+ src = Buf->CursorBitmap->Data + src_y * Buf->CursorBitmap->W + src_x;
+
+ // Allocate save buffer if not already
+ if( !Buf->CursorSaveBuf )
+ Buf->CursorSaveBuf = malloc( Buf->CursorBitmap->W*Buf->CursorBitmap->H*bytes_per_px );
+
+ // Save behind the cursor
+ for( y = 0; y < render_h; y ++ )
+ memcpy(
+ (Uint8*)Buf->CursorSaveBuf + y*save_pitch,
+ (Uint8*)dest + y*Buf->Pitch,
+ render_w*bytes_per_px
+ );
+
+ // Draw the cursor
+ switch(Buf->Depth)
+ {
+ case 15:
+ case 16:
+ Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in cursor draw");
+ break;
+ case 24:
+ for( y = 0; y < render_h; y ++ )
+ {
+ Uint8 *px;
+ px = dest;
+ for(x = 0; x < render_w; x ++, px += 3)
+ {
+ Uint32 value = src[x];
+ // TODO: Should I implement alpha blending?
+ if(value & 0xFF000000)
+ {
+ px[0] = value & 0xFF;
+ px[1] = (value >> 8) & 0xFF;
+ px[2] = (value >> 16) & 0xFF;
+ }
+ else
+ ;
+ }
+ src += Buf->CursorBitmap->W;
+ dest = (Uint8*)dest + Buf->Pitch;
+ }
+ break;
+ case 32:
+ for( y = 0; y < render_h; y ++ )
+ {
+ Uint32 *px;
+ px = dest;
+ for(x = 0; x < render_w; x ++, px ++)
+ {
+ Uint32 value = src[x];
+ // TODO: Should I implement alpha blending?
+ if(value & 0xFF000000)
+ *px = value;
+ else
+ ; // NOP, completely transparent
+ }
+ src += Buf->CursorBitmap->W;
+ dest = (Uint8*)dest + Buf->Pitch;
+ }
+ break;
+ default:
+ Log_Error("DrvUtil", "RenderCursor - Unknown bit depth %i", Buf->Depth);
+ Buf->CursorX = -1;
+ break;
+ }
+}
+
+void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf)
+{
+ int bytes_per_px = (Buf->Depth + 7) / 8;
+ int y, save_pitch;
+ Uint8 *dest, *src;
+
+ // Just a little sanity
+ if( !Buf->CursorBitmap || Buf->CursorX == -1 ) return ;
+ if( !Buf->CursorSaveBuf ) return ;
+
+// Debug("DrvUtil_Video_RemoveCursor: (Buf=%p) dest_x=%i, dest_y=%i", Buf, Buf->CursorDestX, Buf->CursorDestY);
+
+ // Set up
+ save_pitch = Buf->CursorBitmap->W * bytes_per_px;
+ dest = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px;
+ src = Buf->CursorSaveBuf;
+
+ // Copy each line back
+ for( y = 0; y < Buf->CursorRenderH; y ++ )
+ {
+ memcpy( dest, src, Buf->CursorRenderW * bytes_per_px );
+ src += save_pitch;
+ dest += Buf->Pitch;
+ }
+
+ // Set the cursor as removed
+ Buf->CursorX = -1;
+}
+