Modules/VESA - Hide cursor when not LFB mode
[tpg/acess2.git] / KernelLand / Modules / Display / VESA / main.c
1 /*\r
2  * AcessOS 1\r
3  * Video BIOS Extensions (Vesa) Driver\r
4  */\r
5 #define DEBUG   1\r
6 #define VERSION 0x100\r
7 \r
8 #include <acess.h>\r
9 #include <vfs.h>\r
10 #include <api_drv_video.h>\r
11 #include <fs_devfs.h>\r
12 #include <modules.h>\r
13 #include <vm8086.h>\r
14 #include "common.h"\r
15 #include <timers.h>\r
16 \r
17 // === CONSTANTS ===\r
18 #define FLAG_LFB        0x1\r
19 #define FLAG_POPULATED  0x2\r
20 #define FLAG_VALID      0x4\r
21 #define VESA_DEFAULT_FRAMEBUFFER        (KERNEL_BASE|0xA0000)\r
22 #define BLINKING_CURSOR 0\r
23 #if BLINKING_CURSOR\r
24 # define VESA_CURSOR_PERIOD     1000\r
25 #endif\r
26 \r
27 // === PROTOTYPES ===\r
28  int    Vesa_Install(char **Arguments);\r
29  int    VBE_int_GetModeList(void);\r
30 size_t  Vesa_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags);\r
31  int    Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
32  int    Vesa_Int_SetMode(int Mode);\r
33  int    Vesa_Int_FindMode(tVideo_IOCtl_Mode *data);\r
34  int    Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data);\r
35 void    Vesa_int_HideCursor(void);\r
36 void    Vesa_int_ShowCursor(void);\r
37 void    Vesa_FlipCursor(void *Arg);\r
38 Uint16  VBE_int_GetWord(const tVT_Char *Char);\r
39 void    VBE_int_Text_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);\r
40 void    VBE_int_Text_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);\r
41 \r
42 // === GLOBALS ===\r
43 MODULE_DEFINE(0, VERSION, Vesa, Vesa_Install, NULL, "PCI", "VM8086", NULL);\r
44 tVFS_NodeType   gVesa_NodeType = {\r
45         .Write = Vesa_Write,\r
46         .IOCtl = Vesa_IOCtl\r
47         };\r
48 tDevFS_Driver   gVesa_DriverStruct = {\r
49         NULL, "Vesa",\r
50         {.Type = &gVesa_NodeType}\r
51         };\r
52 tMutex  glVesa_Lock;\r
53 tVM8086 *gpVesa_BiosState;\r
54  int    giVesaDriverId = -1;\r
55 // --- Video Modes ---\r
56  int    giVesaCurrentMode = 0;\r
57 tVesa_Mode      gVesa_BootMode = {0x03, 80*8, 25*16, 80*8*2, 12, FLAG_POPULATED, 80*25*2, 0xB8000};\r
58 tVesa_Mode      *gVesa_Modes;\r
59 tVesa_Mode      *gpVesaCurMode;\r
60  int    giVesaModeCount = 0;\r
61  int    gbVesaModesChecked;\r
62 // --- Framebuffer ---\r
63 char    *gpVesa_Framebuffer = (void*)VESA_DEFAULT_FRAMEBUFFER;\r
64  int    giVesaPageCount = 0;    //!< Framebuffer size in pages\r
65 // --- Cursor Control ---\r
66  int    giVesaCursorX = -1;\r
67  int    giVesaCursorY = -1;\r
68 #if BLINKING_CURSOR\r
69 tTimer  *gpVesaCursorTimer;\r
70 #endif\r
71  int    gbVesa_CursorVisible = 0;\r
72 // --- 2D Video Stream Handlers ---\r
73 tDrvUtil_Video_BufInfo  gVesa_BufInfo;\r
74 tDrvUtil_Video_2DHandlers       gVBE_Text2DFunctions = {\r
75         NULL,\r
76         VBE_int_Text_2D_Fill,\r
77         VBE_int_Text_2D_Blit\r
78 };\r
79 // --- Settings ---\r
80 bool    gbVesa_DisableBIOSCalls;        // Disables calls to the BIOS\r
81 // int  gbVesa_DisableFBCache;  // Disables the main-memory framebuffer cache\r
82 \r
83 // === CODE ===\r
84 int Vesa_Install(char **Arguments)\r
85 {\r
86          int    rv;\r
87 \r
88         for( int i = 0; Arguments && Arguments[i]; i ++ )\r
89         {\r
90                 if( strcmp(Arguments[i], "nobios") == 0 )\r
91                         gbVesa_DisableBIOSCalls = 1;\r
92                 //else if( strcmp(Arguments[i], "nocache") == 0 )\r
93                 //      gbVesa_DisableFBCache = 1;\r
94                 else {\r
95                         Log_Notice("VBE", "Unknown argument '%s'", Arguments[i]);\r
96                 }\r
97         }\r
98 \r
99         if( !gbVesa_DisableBIOSCalls )\r
100         {\r
101                 gpVesa_BiosState = VM8086_Init();\r
102                 \r
103                 if( (rv = VBE_int_GetModeList()) )\r
104                         return rv;\r
105         }\r
106                 \r
107         #if BLINKING_CURSOR\r
108         // Create blink timer\r
109         gpVesaCursorTimer = Time_AllocateTimer( Vesa_FlipCursor, NULL );\r
110         #endif\r
111 \r
112         // Install Device\r
113         giVesaDriverId = DevFS_AddDevice( &gVesa_DriverStruct );\r
114         if(giVesaDriverId == -1)        return MODULE_ERR_MISC;\r
115 \r
116         return MODULE_ERR_OK;\r
117 }\r
118 \r
119 int VBE_int_GetModeList(void)\r
120 {\r
121         tVesa_CallInfo  *info;\r
122         tFarPtr infoPtr;\r
123         Uint16  *modes;\r
124         \r
125         // Allocate Info Block\r
126         info = VM8086_Allocate(gpVesa_BiosState, 512, &infoPtr.seg, &infoPtr.ofs);\r
127         // Set Requested Version\r
128         memcpy(info->signature, "VBE2", 4);\r
129         // Set Registers\r
130         gpVesa_BiosState->AX = 0x4F00;\r
131         gpVesa_BiosState->ES = infoPtr.seg;     gpVesa_BiosState->DI = infoPtr.ofs;\r
132         // Call Interrupt\r
133         VM8086_Int(gpVesa_BiosState, 0x10);\r
134         if(gpVesa_BiosState->AX != 0x004F) {\r
135                 Log_Warning("VBE", "Vesa_Install - VESA/VBE Unsupported (AX = 0x%x)", gpVesa_BiosState->AX);\r
136                 return MODULE_ERR_NOTNEEDED;\r
137         }\r
138         \r
139         modes = (Uint16 *) VM8086_GetPointer(gpVesa_BiosState, info->VideoModes.seg, info->VideoModes.ofs);\r
140         LOG("Virtual addres of mode list from %04x:%04x is %p",\r
141                 info->VideoModes.seg, info->VideoModes.ofs, modes);\r
142 //      VM8086_Deallocate( gpVesa_BiosState, info );\r
143         \r
144         // Count Modes\r
145         for( giVesaModeCount = 0; modes[giVesaModeCount] != 0xFFFF; giVesaModeCount++ )\r
146                 ;\r
147         gVesa_Modes = (tVesa_Mode *)malloc( giVesaModeCount * sizeof(tVesa_Mode) );\r
148         \r
149         Log_Debug("VBE", "%i Modes", giVesaModeCount);\r
150 \r
151         // Insert Text Mode\r
152         \r
153         for( int i = 0; i < giVesaModeCount; i++ )\r
154         {\r
155                 gVesa_Modes[i].code = modes[i];\r
156         }\r
157         \r
158         return 0;\r
159 }\r
160 \r
161 int VBE_int_GetModeInfo(Uint16 Code, tFarPtr *BufPtr)\r
162 {\r
163         // Get Mode info\r
164         gpVesa_BiosState->AX = 0x4F01;\r
165         gpVesa_BiosState->CX = Code;\r
166         gpVesa_BiosState->ES = BufPtr->seg;\r
167         gpVesa_BiosState->DI = BufPtr->ofs;\r
168         VM8086_Int(gpVesa_BiosState, 0x10);\r
169 \r
170         if( gpVesa_BiosState->AX != 0x004F ) {\r
171                 Log_Error("VBE", "Getting info on mode 0x%x failed (AX=0x%x)",\r
172                         Code, gpVesa_BiosState->AX);\r
173                 return 1;\r
174         }\r
175         return 0;\r
176 }\r
177 \r
178 \r
179 void VBE_int_FillMode_Int(tVesa_Mode *Mode, const tVesa_CallModeInfo *vbeinfo)\r
180 {\r
181         #if 1\r
182         #define S_LOG(s, fld, fmt)      LOG(" ."#fld" = "fmt, (s).fld)\r
183         LOG("vbeinfo[0x%x] = {", Mode->code);\r
184         S_LOG(*vbeinfo, attributes, "0x%02x");\r
185         S_LOG(*vbeinfo, winA, "0x%02x");\r
186         S_LOG(*vbeinfo, winB, "0x%02x");\r
187         S_LOG(*vbeinfo, granularity, "0x%04x");\r
188         S_LOG(*vbeinfo, winsize, "0x%04x");\r
189         S_LOG(*vbeinfo, segmentA, "0x%04x");\r
190         S_LOG(*vbeinfo, segmentB, "0x%04x");\r
191         LOG(" .realFctPtr = %04x:%04x", vbeinfo->realFctPtr.seg, vbeinfo->realFctPtr.ofs);\r
192         S_LOG(*vbeinfo, pitch, "0x%04x");\r
193 \r
194         // -- Extended\r
195         S_LOG(*vbeinfo, Xres, "%i");\r
196         S_LOG(*vbeinfo, Yres, "%i");\r
197         S_LOG(*vbeinfo, Wchar, "%i");\r
198         S_LOG(*vbeinfo, Ychar, "%i");\r
199         S_LOG(*vbeinfo, planes, "%i");\r
200         S_LOG(*vbeinfo, bpp, "%i");\r
201         S_LOG(*vbeinfo, banks, "%i");\r
202         S_LOG(*vbeinfo, memory_model, "%i");\r
203         S_LOG(*vbeinfo, bank_size, "%i");\r
204         S_LOG(*vbeinfo, image_pages, "%i");\r
205         // -- VBE 1.2+\r
206         LOG(" Red   = %i bits at %i", vbeinfo->red_mask,   vbeinfo->red_position  );\r
207         LOG(" Green = %i bits at %i", vbeinfo->green_mask, vbeinfo->green_position);\r
208         LOG(" Blue  = %i bits at %i", vbeinfo->blue_mask,  vbeinfo->blue_position );\r
209         #if 0\r
210         Uint8   rsv_mask, rsv_position;\r
211         Uint8   directcolor_attributes;\r
212         #endif\r
213         // --- VBE 2.0+\r
214         S_LOG(*vbeinfo, physbase, "0x%08x");\r
215         S_LOG(*vbeinfo, offscreen_ptr, "0x%08x");\r
216         S_LOG(*vbeinfo, offscreen_size_kb, "0x%04x");\r
217         // --- VBE 3.0+\r
218         S_LOG(*vbeinfo, lfb_pitch, "0x%04x");\r
219         S_LOG(*vbeinfo, image_count_banked, "%i");\r
220         S_LOG(*vbeinfo, image_count_lfb, "%i");\r
221         LOG("}");\r
222         #endif\r
223 \r
224         Mode->flags = FLAG_POPULATED;\r
225         if( !(vbeinfo->attributes & 1) )\r
226         {\r
227                 #if DEBUG\r
228                 Log_Log("VBE", "0x%x - not supported", Mode->code);\r
229                 #endif\r
230                 Mode->width = 0;\r
231                 Mode->height = 0;\r
232                 Mode->bpp = 0;\r
233                 return ;\r
234         }\r
235 \r
236         // Parse Info\r
237         Mode->flags |= FLAG_VALID;\r
238         switch( vbeinfo->attributes & 0x90 )    // LFB, Graphics\r
239         {\r
240         case 0x00:      // Banked, Text\r
241         case 0x10:      // Banked, Graphics\r
242         case 0x80:      // Linear, Text (?)\r
243                 Mode->width = 0;\r
244                 Mode->height = 0;\r
245                 Mode->bpp = 0;\r
246                 return ;\r
247         case 0x90:\r
248                 Mode->flags |= FLAG_LFB;\r
249                 Mode->framebuffer = vbeinfo->physbase;\r
250                 Mode->fbSize = vbeinfo->Yres*vbeinfo->pitch;\r
251                 break;\r
252         }\r
253         \r
254         Mode->pitch = vbeinfo->pitch;\r
255         Mode->width = vbeinfo->Xres;\r
256         Mode->height = vbeinfo->Yres;\r
257         Mode->bpp = vbeinfo->bpp;\r
258         \r
259         #if DEBUG\r
260         Log_Log("VBE", "0x%x - %ix%ix%i (%x)",\r
261                 Mode->code, Mode->width, Mode->height, Mode->bpp, Mode->flags);\r
262         #endif\r
263 \r
264\r
265 \r
266 void VBE_int_SetBootMode(Uint16 ModeID, const void *ModeInfo)\r
267 {\r
268         gVesa_BootMode.code = ModeID;\r
269         VBE_int_FillMode_Int(&gVesa_BootMode, ModeInfo);\r
270 }\r
271 \r
272 void Vesa_int_FillModeList(void)\r
273 {\r
274         if( !gbVesaModesChecked && !gbVesa_DisableBIOSCalls )\r
275         {\r
276                 tVesa_CallModeInfo      *modeinfo;\r
277                 tFarPtr modeinfoPtr;\r
278                 \r
279                 modeinfo = VM8086_Allocate(gpVesa_BiosState, 256, &modeinfoPtr.seg, &modeinfoPtr.ofs);\r
280                 for( int i = 0; i < giVesaModeCount; i ++ )\r
281                 {\r
282                         if( VBE_int_GetModeInfo(gVesa_Modes[i].code, &modeinfoPtr) == 0 )\r
283                         {\r
284                                 VBE_int_FillMode_Int( &gVesa_Modes[i], modeinfo );\r
285                         }\r
286                 }       \r
287 //              VM8086_Deallocate( gpVesa_BiosState, modeinfo );\r
288                 \r
289                 gbVesaModesChecked = 1;\r
290         }\r
291 }\r
292 \r
293 /**\r
294  * \brief Write to the framebuffer\r
295  */\r
296 size_t Vesa_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)\r
297 {\r
298         // Framebuffer modes - just pass on\r
299         if( gpVesaCurMode->flags & FLAG_LFB )\r
300                 return DrvUtil_Video_WriteLFB(&gVesa_BufInfo, Offset, Length, Buffer);\r
301         \r
302         // EGA text mode translation\r
303         switch( gVesa_BufInfo.BufferFormat )\r
304         {\r
305         case VIDEO_BUFFMT_TEXT: {\r
306                  int    num = Length / sizeof(tVT_Char);\r
307                  int    ofs = Offset / sizeof(tVT_Char);\r
308                  int    i = 0;\r
309                 const tVT_Char  *chars = Buffer;\r
310                 Uint16  word;\r
311                 \r
312                 for( ; num--; i ++, ofs ++)\r
313                 {\r
314                         word = VBE_int_GetWord( &chars[i] );\r
315                         ((Uint16*)gVesa_BufInfo.Framebuffer)[ ofs ] = word;\r
316                 }\r
317                 \r
318                 return Length; }\r
319         case VIDEO_BUFFMT_2DSTREAM:\r
320                 return DrvUtil_Video_2DStream(NULL, Buffer, Length,\r
321                         &gVBE_Text2DFunctions, sizeof(gVBE_Text2DFunctions));\r
322         default:\r
323                 Log_Warning("VBE", "TODO: Alternate modes in EGA text mode");\r
324                 return 0;\r
325         }\r
326 \r
327 }\r
328 \r
329 const char *csaVESA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
330 /**\r
331  * \brief Handle messages to the device\r
332  */\r
333 int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
334 {\r
335          int    ret;\r
336         switch(ID)\r
337         {\r
338         BASE_IOCTLS(DRV_TYPE_VIDEO, "VBE", VERSION, csaVESA_IOCtls);\r
339 \r
340         case VIDEO_IOCTL_GETSETMODE:\r
341                 if( !Data )     return giVesaCurrentMode;\r
342                 return Vesa_Int_SetMode( *(int*)Data );\r
343         \r
344         case VIDEO_IOCTL_FINDMODE:\r
345                 return Vesa_Int_FindMode((tVideo_IOCtl_Mode*)Data);\r
346         case VIDEO_IOCTL_MODEINFO:\r
347                 return Vesa_Int_ModeInfo((tVideo_IOCtl_Mode*)Data);\r
348         \r
349         case VIDEO_IOCTL_SETBUFFORMAT:\r
350                 Vesa_int_HideCursor();\r
351                 ret = gVesa_BufInfo.BufferFormat;\r
352                 if(Data)        gVesa_BufInfo.BufferFormat = *(int*)Data;\r
353                 if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
354                         DrvUtil_Video_SetCursor( &gVesa_BufInfo, &gDrvUtil_TextModeCursor );\r
355                 Vesa_int_ShowCursor();\r
356                 return ret;\r
357         \r
358         case VIDEO_IOCTL_SETCURSOR:     // Set cursor position\r
359                 Vesa_int_HideCursor();\r
360                 giVesaCursorX = ((tVideo_IOCtl_Pos*)Data)->x;\r
361                 giVesaCursorY = ((tVideo_IOCtl_Pos*)Data)->y;\r
362                 Vesa_int_ShowCursor();\r
363                 return 0;\r
364         \r
365         case VIDEO_IOCTL_SETCURSORBITMAP:\r
366                 if( gpVesaCurMode->flags & FLAG_LFB )\r
367                         DrvUtil_Video_SetCursor( &gVesa_BufInfo, Data );\r
368                 return 0;\r
369         }\r
370         return 0;\r
371 }\r
372 \r
373 /**\r
374  * \brief Updates the video mode\r
375  */\r
376 int Vesa_Int_SetMode(int mode)\r
377 {\r
378         tVesa_Mode      *modeptr;\r
379         // Check for fast return\r
380         if(mode == giVesaCurrentMode)   return 1;\r
381         \r
382         // Special case: Boot mode\r
383         if( mode == -1 )\r
384                 modeptr = &gVesa_BootMode;\r
385         else if( 0 <= mode && mode < giVesaModeCount )\r
386                 modeptr = &gVesa_Modes[mode];\r
387         else\r
388                 return -1;\r
389         \r
390         Vesa_int_FillModeList();\r
391 \r
392         #if BLINKING_CURSOR\r
393         Time_RemoveTimer(gpVesaCursorTimer);\r
394         #endif\r
395         \r
396         Mutex_Acquire( &glVesa_Lock );\r
397         \r
398         if( gbVesa_DisableBIOSCalls )\r
399         {\r
400                 ASSERT(mode == -1);\r
401         }\r
402         else\r
403         {\r
404                 gpVesa_BiosState->AX = 0x4F02;\r
405                 gpVesa_BiosState->BX = modeptr->code;\r
406                 if(modeptr->flags & FLAG_LFB) {\r
407                         gpVesa_BiosState->BX |= 1 << 14;        // Use LFB\r
408                 }\r
409                 LOG("In : AX=%04x/BX=%04x", gpVesa_BiosState->AX, gpVesa_BiosState->BX);\r
410                 \r
411                 // Set Mode\r
412                 VM8086_Int(gpVesa_BiosState, 0x10);\r
413 \r
414                 LOG("Out: AX = %04x", gpVesa_BiosState->AX);\r
415         }\r
416         \r
417         // Map Framebuffer\r
418         if( gpVesaCurMode )\r
419         {\r
420                 if( gpVesaCurMode->framebuffer < 1024*1024 )\r
421                         ;\r
422                 else\r
423                         MM_UnmapHWPages((tVAddr)gpVesa_Framebuffer, giVesaPageCount);\r
424         }\r
425         giVesaPageCount = (modeptr->fbSize + 0xFFF) >> 12;\r
426         if( modeptr->framebuffer < 1024*1024 )\r
427                 gpVesa_Framebuffer = (void*)(KERNEL_BASE|modeptr->framebuffer);\r
428         else\r
429                 gpVesa_Framebuffer = (void*)MM_MapHWPages(modeptr->framebuffer, giVesaPageCount);\r
430         \r
431         Log_Log("VBE", "Setting mode to %i 0x%x (%ix%i %ibpp) %p[0x%x] maps %P",\r
432                 mode, modeptr->code,\r
433                 modeptr->width, modeptr->height,\r
434                 modeptr->bpp,\r
435                 gpVesa_Framebuffer, giVesaPageCount << 12, modeptr->framebuffer\r
436                 );\r
437         \r
438         // Record Mode Set\r
439         giVesaCurrentMode = mode;\r
440         gpVesaCurMode = modeptr;\r
441         \r
442         Mutex_Release( &glVesa_Lock );\r
443 \r
444         // TODO: Disableable backbuffer\r
445         gVesa_BufInfo.BackBuffer  = realloc(gVesa_BufInfo.BackBuffer,\r
446                 modeptr->height * modeptr->pitch);\r
447         gVesa_BufInfo.Framebuffer = gpVesa_Framebuffer;\r
448         gVesa_BufInfo.Pitch = modeptr->pitch;\r
449         gVesa_BufInfo.Width = modeptr->width;\r
450         gVesa_BufInfo.Height = modeptr->height;\r
451         gVesa_BufInfo.Depth = modeptr->bpp;     \r
452 \r
453         return 1;\r
454 }\r
455 \r
456 int VBE_int_MatchModes(tVideo_IOCtl_Mode *ReqMode, tVesa_Mode *ThisMode)\r
457 {\r
458         LOG("Matching %ix%i %ibpp", ThisMode->width, ThisMode->height, ThisMode->bpp);\r
459         if(ThisMode->width == ReqMode->width && ThisMode->height == ReqMode->height)\r
460         {\r
461                 //if( (data->bpp == 32 || data->bpp == 24)\r
462                 // && (gVesa_Modes[i].bpp == 32 || gVesa_Modes[i].bpp == 24) )\r
463                 if( ReqMode->bpp == ThisMode->bpp )\r
464                 {\r
465                         LOG("Perfect!");\r
466                         return -1;\r
467                 }\r
468         }\r
469         \r
470         int tmp = ThisMode->width * ThisMode->height - ReqMode->width * ReqMode->height;\r
471         tmp = tmp < 0 ? -tmp : tmp;\r
472         unsigned int factor = (Uint64)tmp * 1000 / (ReqMode->width * ReqMode->height);\r
473         if( ThisMode->bpp > ReqMode->bpp )\r
474                 factor += ThisMode->bpp - ReqMode->bpp;\r
475         else\r
476                 factor += ReqMode->bpp - ThisMode->bpp;\r
477 \r
478         if( ReqMode->bpp == ThisMode->bpp )\r
479                 factor /= 2;\r
480         else\r
481         {\r
482                 if( ReqMode->bpp == 8 && ThisMode->bpp != 8 )   factor *= 4;\r
483                 if( ReqMode->bpp == 16 && ThisMode->bpp != 16 ) factor *= 4;\r
484                 \r
485                 if( (ReqMode->bpp == 32 || ReqMode->bpp == 24)\r
486                  && (ThisMode->bpp == 32 || ThisMode->bpp == 24) )\r
487                 {\r
488                         // NC\r
489                 }\r
490                 else {\r
491                         if( ReqMode->bpp < ThisMode->bpp )\r
492                                 factor *= ThisMode->bpp / ReqMode->bpp + 1;\r
493                         else\r
494                                 factor *= ReqMode->bpp / ThisMode->bpp + 1;\r
495                 }\r
496         }\r
497         \r
498         return factor;\r
499 }\r
500 \r
501 int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data)\r
502 {\r
503          int    best = -1;\r
504         \r
505         ENTER("idata->width idata->height idata->bpp", data->width, data->height, data->bpp);\r
506 \r
507         Vesa_int_FillModeList();\r
508 \r
509         int bestFactor = VBE_int_MatchModes(data, &gVesa_BootMode);     \r
510         tVesa_Mode *bestPtr = &gVesa_BootMode;\r
511 \r
512         for(int i = 0; bestFactor > 0 && i < giVesaModeCount; i ++)\r
513         {\r
514                 LOG("Mode %i (%ix%ix%i)", i, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp);\r
515         \r
516                 int factor = VBE_int_MatchModes(data, &gVesa_Modes[i]);\r
517                 \r
518                 LOG("factor = %i, bestFactor = %i", factor, bestFactor);\r
519                 \r
520                 if(factor < bestFactor)\r
521                 {\r
522                         bestFactor = factor;\r
523                         best = i;\r
524                         bestPtr = &gVesa_Modes[i];\r
525                 }\r
526         }\r
527         data->id = best;\r
528         data->width = bestPtr->width;\r
529         data->height = bestPtr->height;\r
530         data->bpp = bestPtr->bpp;\r
531         LEAVE('i', best);\r
532         return best;\r
533 }\r
534 \r
535 int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data)\r
536 {\r
537         tVesa_Mode      *modeptr;\r
538         if( data->id == -1 )\r
539                 modeptr = &gVesa_BootMode;\r
540         else if( 0 <= data->id && data->id < giVesaModeCount)\r
541                 modeptr = &gVesa_Modes[data->id];\r
542         else\r
543                 return 0;\r
544 \r
545         Vesa_int_FillModeList();\r
546 \r
547         data->width  = modeptr->width;\r
548         data->height = modeptr->height;\r
549         data->bpp    = modeptr->bpp;\r
550         return 1;\r
551 }\r
552 \r
553 void Vesa_int_HideCursor(void)\r
554 {\r
555         DrvUtil_Video_RemoveCursor( &gVesa_BufInfo );\r
556         if( gpVesaCurMode->flags & FLAG_LFB )\r
557         {\r
558                 #if BLINKING_CURSOR\r
559                 if(gpVesaCursorTimer) {\r
560                         Time_RemoveTimer(gpVesaCursorTimer);\r
561                 }\r
562                 #endif\r
563         }\r
564 }\r
565 \r
566 void Vesa_int_ShowCursor(void)\r
567 {\r
568         if( gpVesaCurMode->flags & FLAG_LFB )\r
569         {\r
570                 gbVesa_CursorVisible = (giVesaCursorX >= 0);\r
571                 if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
572                 {\r
573                         DrvUtil_Video_DrawCursor(\r
574                                 &gVesa_BufInfo,\r
575                                 giVesaCursorX*giVT_CharWidth,\r
576                                 giVesaCursorY*giVT_CharHeight\r
577                                 );\r
578                         #if BLINKING_CURSOR\r
579                         Time_ScheduleTimer( gpVesaCursorTimer, VESA_CURSOR_PERIOD );\r
580                         #endif\r
581                 }\r
582                 else\r
583                         DrvUtil_Video_DrawCursor(\r
584                                 &gVesa_BufInfo,\r
585                                 giVesaCursorX,\r
586                                 giVesaCursorY\r
587                                 );\r
588         }\r
589         else\r
590         {\r
591                 DrvUtil_Video_RemoveCursor( &gVesa_BufInfo );\r
592         }\r
593 }\r
594 \r
595 /**\r
596  * \brief Swaps the text cursor on/off\r
597  */\r
598 void Vesa_FlipCursor(void *Arg)\r
599 {\r
600         if( gVesa_BufInfo.BufferFormat != VIDEO_BUFFMT_TEXT )\r
601                 return ;\r
602 \r
603         if( gpVesaCurMode->flags & FLAG_LFB )\r
604         {\r
605                 if( gbVesa_CursorVisible )\r
606                         DrvUtil_Video_RemoveCursor(&gVesa_BufInfo);\r
607                 else\r
608                         DrvUtil_Video_DrawCursor(&gVesa_BufInfo,\r
609                                 giVesaCursorX*giVT_CharWidth,\r
610                                 giVesaCursorY*giVT_CharHeight\r
611                                 );\r
612                 gbVesa_CursorVisible = !gbVesa_CursorVisible;\r
613                         \r
614                 #if BLINKING_CURSOR\r
615                 Time_ScheduleTimer( gpVesaCursorTimer, VESA_CURSOR_PERIOD );\r
616                 #endif\r
617         }\r
618 }\r
619 \r
620 // ---\r
621 // Helpers for text mode\r
622 // ---\r
623 \r
624 /**\r
625  * \fn Uint8 VGA_int_GetColourNibble(Uint16 col)\r
626  * \brief Converts a 12-bit colour into a VGA 4-bit colour\r
627  */\r
628 Uint8 VBE_int_GetColourNibble(Uint16 col)\r
629 {\r
630         Uint8   ret = 0;\r
631          int    bright = 0;\r
632         \r
633         col = col & 0xCCC;\r
634         col = ((col>>2)&3) | ((col>>4)&0xC) | ((col>>6)&0x30);\r
635         bright = ( (col & 2 ? 1 : 0) + (col & 8 ? 1 : 0) + (col & 32 ? 1 : 0) ) / 2;\r
636         \r
637         switch(col)\r
638         {\r
639         //      Black\r
640         case 0x00:      ret = 0x0;      break;\r
641         // Dark Grey\r
642         case 0x15:      ret = 0x8;      break;\r
643         // Blues\r
644         case 0x01:\r
645         case 0x02:      ret = 0x1;      break;\r
646         case 0x03:      ret = 0x9;      break;\r
647         // Green\r
648         case 0x04:\r
649         case 0x08:      ret = 0x2;      break;\r
650         case 0x0C:      ret = 0xA;      break;\r
651         // Reds\r
652         case 0x10:\r
653         case 0x20:      ret = 0x4;      break;\r
654         case 0x30:      ret = 0xC;      break;\r
655         // Light Grey\r
656         case 0x2A:      ret = 0x7;      break;\r
657         // White\r
658         case 0x3F:      ret = 0xF;      break;\r
659         \r
660         default:\r
661                 ret |= (col & 0x03 ? 1 : 0);\r
662                 ret |= (col & 0x0C ? 2 : 0);\r
663                 ret |= (col & 0x30 ? 4 : 0);\r
664                 ret |= (bright ? 8 : 0);\r
665                 break;\r
666         }\r
667         return ret;\r
668 }\r
669 \r
670 /**\r
671  * \brief Convers a character structure to a VGA character word\r
672  */\r
673 Uint16 VBE_int_GetWord(const tVT_Char *Char)\r
674 {\r
675         Uint16  ret;\r
676         Uint16  col;\r
677         \r
678         // Get Character\r
679         if(Char->Ch < 128)\r
680                 ret = Char->Ch;\r
681         else {\r
682                 switch(Char->Ch)\r
683                 {\r
684                 default:        ret = 0;        break;\r
685                 }\r
686         }\r
687         \r
688         col = VBE_int_GetColourNibble(Char->BGCol);\r
689         ret |= col << 12;\r
690         \r
691         col = VBE_int_GetColourNibble(Char->FGCol);\r
692         ret |= col << 8;\r
693         \r
694         return ret;\r
695 }\r
696 \r
697 void VBE_int_Text_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour)\r
698 {\r
699         const int       charw = 8;\r
700         const int       charh = 16;\r
701         const int       tw = gpVesaCurMode->width / charw;\r
702         const int       th = gpVesaCurMode->height / charh;\r
703 \r
704         X /= charw;\r
705         W /= charw;\r
706         Y /= charh;\r
707         H /= charh;\r
708 \r
709         tVT_Char        ch;\r
710         ch.Ch = 0x20;\r
711         ch.BGCol  = (Colour & 0x0F0000) >> (16-8);\r
712         ch.BGCol |= (Colour & 0x000F00) >> (8-4);\r
713         ch.BGCol |= (Colour & 0x00000F);\r
714         ch.FGCol = 0;\r
715         Uint16 word = VBE_int_GetWord(&ch);\r
716 \r
717         Log("Fill (%i,%i) %ix%i with 0x%x", X, Y, W, H, word);\r
718 \r
719         if( X >= tw || Y >= th )        return ;\r
720         if( X + W > tw )        W = tw - X;\r
721         if( Y + H > th )        H = th - Y;\r
722 \r
723         Uint16  *buf = (Uint16*)gpVesa_Framebuffer + Y*tw + X;\r
724 \r
725         \r
726         while( H -- ) {\r
727                 for( int i = 0; i < W; i ++ )\r
728                         *buf++ = word;\r
729                 buf += tw - W;\r
730         }\r
731 }\r
732 \r
733 void VBE_int_Text_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H)\r
734 {\r
735         const int       charw = 8;\r
736         const int       charh = 16;\r
737         const int       tw = gpVesaCurMode->width / charw;\r
738         const int       th = gpVesaCurMode->height / charh;\r
739 \r
740         DstX /= charw;\r
741         SrcX /= charw;\r
742         W    /= charw;\r
743 \r
744         DstY /= charh;\r
745         SrcY /= charh;\r
746         H    /= charh;\r
747 \r
748 //      Log("(%i,%i) from (%i,%i) %ix%i", DstX, DstY, SrcX, SrcY, W, H);\r
749 \r
750         if( SrcX >= tw || SrcY >= th )  return ;\r
751         if( SrcX + W > tw )     W = tw - SrcX;\r
752         if( SrcY + H > th )     H = th - SrcY;\r
753         if( DstX >= tw || DstY >= th )  return ;\r
754         if( DstX + W > tw )     W = tw - DstX;\r
755         if( DstY + H > th )     H = th - DstY;\r
756 \r
757 \r
758         Uint16  *src = (Uint16*)gpVesa_Framebuffer + SrcY*tw + SrcX;\r
759         Uint16  *dst = (Uint16*)gpVesa_Framebuffer + DstY*tw + DstX;\r
760 \r
761         if( src > dst )\r
762         {\r
763                 // Simple copy\r
764                 while( H-- ) {\r
765                         memcpy(dst, src, W*2);\r
766                         dst += tw;\r
767                         src += tw;\r
768                 }\r
769         }\r
770         else\r
771         {\r
772                 dst += H*tw;\r
773                 src += H*tw;\r
774                 while( H -- ) {\r
775                         dst -= tw-W;\r
776                         src -= tw-W;\r
777                         for( int i = W; i --; ) *--dst = *--src;\r
778                 }\r
779         }\r
780 }\r
781 \r

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