Documentation name fixes.
[wine] / msdos / vga.c
1 /*
2  * VGA hardware emulation
3  * 
4  * Copyright 1998 Ove Kåven (with some help from Marcus Meissner)
5  *
6  */
7
8 #include <string.h>
9 #include "winbase.h"
10 #include "wingdi.h"
11 #include "winuser.h"
12 #include "wincon.h"
13 #include "miscemu.h"
14 #include "vga.h"
15 #include "ddraw.h"
16 #include "services.h"
17 #include "debugtools.h"
18
19 DEFAULT_DEBUG_CHANNEL(ddraw);
20
21 static IDirectDraw *lpddraw = NULL;
22 static IDirectDrawSurface *lpddsurf;
23 static IDirectDrawPalette *lpddpal;
24 static DDSURFACEDESC sdesc;
25 static LONG vga_polling,vga_refresh;
26 static HANDLE poll_timer;
27
28 typedef HRESULT WINAPI (*DirectDrawCreateProc)(LPGUID,LPDIRECTDRAW *,LPUNKNOWN);
29 static DirectDrawCreateProc pDirectDrawCreate;
30
31 typedef HWND WINAPI (*CreateWindowExAProc)(DWORD,LPCSTR,LPCSTR,DWORD,INT,INT, INT,INT,HWND,HMENU,HINSTANCE,LPVOID);
32 static CreateWindowExAProc pCreateWindowExA;
33
34 static void VGA_DeinstallTimer(void)
35 {
36     if (poll_timer) {
37         SERVICE_Delete( poll_timer );
38         poll_timer = 0;
39     }
40 }
41
42 static void VGA_InstallTimer(unsigned Rate)
43 {
44     VGA_DeinstallTimer();
45     if (!poll_timer)
46         poll_timer = SERVICE_AddTimer( Rate, VGA_Poll, 0 );
47 }
48
49 HANDLE VGA_AlphaConsole(void)
50 {
51     /* this assumes that no Win32 redirection has taken place, but then again,
52      * only 16-bit apps are likely to use this part of Wine... */
53     return GetStdHandle(STD_OUTPUT_HANDLE);
54 }
55
56 char*VGA_AlphaBuffer(void)
57 {
58     return DOSMEM_MapDosToLinear(0xb8000);
59 }
60
61 /*** GRAPHICS MODE ***/
62
63 int VGA_SetMode(unsigned Xres,unsigned Yres,unsigned Depth)
64 {
65     LRESULT     res;
66     HWND        hwnd;
67
68     if (lpddraw) VGA_Exit();
69     if (!lpddraw) {
70         if (!pDirectDrawCreate)
71         {
72             HMODULE hmod = LoadLibraryA( "ddraw.dll" );
73             if (hmod) pDirectDrawCreate = (DirectDrawCreateProc)GetProcAddress( hmod, "DirectDrawCreate" );
74             if (!pDirectDrawCreate) {
75                 ERR("Can't lookup DirectDrawCreate from ddraw.dll.\n");
76                 return 1;
77             }
78         }
79         if (!pCreateWindowExA)
80         {
81             HMODULE hmod = LoadLibraryA( "user32.dll" );
82             if (!hmod) {
83                 ERR("Can't load user32.dll.\n");
84                 return 1;
85             }
86             if (hmod) pCreateWindowExA = (CreateWindowExAProc)GetProcAddress( hmod, "CreateWindowExA" );
87             if (!pCreateWindowExA) {
88                 ERR("Can't lookup CreateWindowExA from user32.dll.\n");
89                 return 1;
90             }
91         }
92         res = pDirectDrawCreate(NULL,&lpddraw,NULL);
93         if (!lpddraw) {
94             ERR("DirectDraw is not available (res = %lx)\n",res);
95             return 1;
96         }
97         hwnd = pCreateWindowExA(0,"STATIC","WINEDOS VGA",WS_POPUP|WS_BORDER|WS_CAPTION|WS_SYSMENU,0,0,Xres,Yres,0,0,0,NULL);
98         if (!hwnd) {
99             ERR("Failed to create user window.\n");
100         }
101         if ((res=IDirectDraw_SetCooperativeLevel(lpddraw,hwnd,DDSCL_FULLSCREEN|DDSCL_EXCLUSIVE))) {
102             ERR("Could not set cooperative level to exclusive (%lx)\n",res);
103         }
104
105         if ((res=IDirectDraw_SetDisplayMode(lpddraw,Xres,Yres,Depth))) {
106             ERR("DirectDraw does not support requested display mode (%dx%dx%d), res = %lx!\n",Xres,Yres,Depth,res);
107             IDirectDraw_Release(lpddraw);
108             lpddraw=NULL;
109             return 1;
110         }
111         res=IDirectDraw_CreatePalette(lpddraw,DDPCAPS_8BIT,NULL,&lpddpal,NULL);
112         if (res) {
113             ERR("Could not create palette (res = %lx)\n",res);
114         }
115         memset(&sdesc,0,sizeof(sdesc));
116         sdesc.dwSize=sizeof(sdesc);
117         sdesc.dwFlags = DDSD_CAPS;
118         sdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
119         if (IDirectDraw_CreateSurface(lpddraw,&sdesc,&lpddsurf,NULL)||(!lpddsurf)) {
120             ERR("DirectDraw surface is not available\n");
121             IDirectDraw_Release(lpddraw);
122             lpddraw=NULL;
123             return 1;
124         }
125         FIXME("no default palette entries\n");
126         IDirectDrawSurface_SetPalette(lpddsurf,lpddpal);
127         vga_refresh=0;
128         /* poll every 20ms (50fps should provide adequate responsiveness) */
129         VGA_InstallTimer(20);
130     }
131     return 0;
132 }
133
134 int VGA_GetMode(unsigned*Height,unsigned*Width,unsigned*Depth)
135 {
136     if (!lpddraw) return 1;
137     if (!lpddsurf) return 1;
138     if (Height) *Height=sdesc.dwHeight;
139     if (Width) *Width=sdesc.dwWidth;
140     if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
141     return 0;
142 }
143
144 void VGA_Exit(void)
145 {
146     if (lpddraw) {
147         VGA_DeinstallTimer();
148         IDirectDrawSurface_SetPalette(lpddsurf,NULL);
149         IDirectDrawSurface_Release(lpddsurf);
150         lpddsurf=NULL;
151         IDirectDrawPalette_Release(lpddpal);
152         lpddpal=NULL;
153         IDirectDraw_Release(lpddraw);
154         lpddraw=NULL;
155     }
156 }
157
158 void VGA_SetPalette(PALETTEENTRY*pal,int start,int len)
159 {
160     if (!lpddraw) return;
161     IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
162 }
163
164 void VGA_SetQuadPalette(RGBQUAD*color,int start,int len)
165 {
166     PALETTEENTRY pal[256];
167     int c;
168
169     if (!lpddraw) return;
170     for (c=0; c<len; c++) {
171         pal[c].peRed  =color[c].rgbRed;
172         pal[c].peGreen=color[c].rgbGreen;
173         pal[c].peBlue =color[c].rgbBlue;
174         pal[c].peFlags=0;
175     }
176     IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
177 }
178
179 LPSTR VGA_Lock(unsigned*Pitch,unsigned*Height,unsigned*Width,unsigned*Depth)
180 {
181     if (!lpddraw) return NULL;
182     if (!lpddsurf) return NULL;
183     if (IDirectDrawSurface_Lock(lpddsurf,NULL,&sdesc,0,0)) {
184         ERR("could not lock surface!\n");
185         return NULL;
186     }
187     if (Pitch) *Pitch=sdesc.u1.lPitch;
188     if (Height) *Height=sdesc.dwHeight;
189     if (Width) *Width=sdesc.dwWidth;
190     if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
191     return sdesc.lpSurface;
192 }
193
194 void VGA_Unlock(void)
195 {
196     IDirectDrawSurface_Unlock(lpddsurf,sdesc.lpSurface);
197 }
198
199 /*** TEXT MODE ***/
200
201 int VGA_SetAlphaMode(unsigned Xres,unsigned Yres)
202 {
203     COORD siz;
204
205     if (lpddraw) VGA_Exit();
206
207     /* the xterm is slow, so refresh only every 200ms (5fps) */
208     VGA_InstallTimer(200);
209
210     siz.X = Xres;
211     siz.Y = Yres;
212     SetConsoleScreenBufferSize(VGA_AlphaConsole(),siz);
213     return 0;
214 }
215
216 void VGA_GetAlphaMode(unsigned*Xres,unsigned*Yres)
217 {
218     CONSOLE_SCREEN_BUFFER_INFO info;
219     GetConsoleScreenBufferInfo(VGA_AlphaConsole(),&info);
220     if (Xres) *Xres=info.dwSize.X;
221     if (Yres) *Yres=info.dwSize.Y;
222 }
223
224 void VGA_SetCursorPos(unsigned X,unsigned Y)
225 {
226     COORD pos;
227     
228     pos.X = X;
229     pos.Y = Y;
230     SetConsoleCursorPosition(VGA_AlphaConsole(),pos);
231 }
232
233 void VGA_GetCursorPos(unsigned*X,unsigned*Y)
234 {
235     CONSOLE_SCREEN_BUFFER_INFO info;
236     GetConsoleScreenBufferInfo(VGA_AlphaConsole(),&info);
237     if (X) *X=info.dwCursorPosition.X;
238     if (Y) *Y=info.dwCursorPosition.Y;
239 }
240
241 void VGA_WriteChars(unsigned X,unsigned Y,unsigned ch,int attr,int count)
242 {
243     unsigned XR, YR;
244     char*dat;
245
246     VGA_GetAlphaMode(&XR, &YR);
247     dat = VGA_AlphaBuffer() + ((XR*Y + X) * 2);
248     /* FIXME: also call WriteConsoleOutputA, for better responsiveness */
249     while (count--) {
250         *dat++ = ch;
251         if (attr>=0) *dat = attr;
252         dat++;
253     }
254 }
255
256 /*** CONTROL ***/
257
258 void CALLBACK VGA_Poll( ULONG_PTR arg )
259 {
260     char *dat;
261     unsigned int Pitch,Height,Width,Y,X;
262     char *surf;
263
264     if (!InterlockedExchangeAdd(&vga_polling, 1)) {
265         /* FIXME: optimize by doing this only if the data has actually changed
266          *        (in a way similar to DIBSection, perhaps) */
267         if (lpddraw) {
268           /* graphics mode */
269           surf = VGA_Lock(&Pitch,&Height,&Width,NULL);
270           if (!surf) return;
271           dat = DOSMEM_MapDosToLinear(0xa0000);
272           /* copy from virtual VGA frame buffer to DirectDraw surface */
273           for (Y=0; Y<Height; Y++,surf+=Pitch,dat+=Width) {
274               memcpy(surf,dat,Width);
275               /*for (X=0; X<Width; X++) if (dat[X]) TRACE(ddraw,"data(%d) at (%d,%d)\n",dat[X],X,Y);*/
276           }
277           VGA_Unlock();
278         } else {
279           /* text mode */
280           CHAR_INFO ch[80];
281           COORD siz, off;
282           SMALL_RECT dest;
283           HANDLE con = VGA_AlphaConsole();
284
285           VGA_GetAlphaMode(&Width,&Height);
286           dat = VGA_AlphaBuffer();
287           siz.X = 80; siz.Y = 1;
288           off.X = 0; off.Y = 0;
289           /* copy from virtual VGA frame buffer to console */
290           for (Y=0; Y<Height; Y++) {
291               dest.Top=Y; dest.Bottom=Y;
292               for (X=0; X<Width; X++) {
293                   ch[X].Char.AsciiChar = *dat++;
294                   /* WriteConsoleOutputA doesn't like "dead" chars */
295                   if (ch[X].Char.AsciiChar == '\0')
296                       ch[X].Char.AsciiChar = ' ';
297                   ch[X].Attributes = *dat++;
298               }
299               dest.Left=0; dest.Right=Width+1;
300               WriteConsoleOutputA(con, ch, siz, off, &dest);
301           }
302         }
303         vga_refresh=1;
304     }
305     InterlockedDecrement(&vga_polling);
306 }
307
308 static BYTE palreg,palcnt;
309 static PALETTEENTRY paldat;
310
311 void VGA_ioport_out( WORD port, BYTE val )
312 {
313     switch (port) {
314         case 0x3c8:
315             palreg=val; palcnt=0; break;
316         case 0x3c9:
317             ((BYTE*)&paldat)[palcnt++]=val << 2;
318             if (palcnt==3) {
319                 VGA_SetPalette(&paldat,palreg++,1);
320                 palcnt=0;
321             }
322             break;
323     }
324 }
325
326 BYTE VGA_ioport_in( WORD port )
327 {
328     BYTE ret;
329
330     switch (port) {
331         case 0x3da:
332             /* since we don't (yet?) serve DOS VM requests while VGA_Poll is running,
333                we need to fake the occurrence of the vertical refresh */
334             ret=vga_refresh?0x00:0x08;
335             vga_refresh=0;
336             break;
337         default:
338             ret=0xff;
339     }
340     return ret;
341 }
342
343 void VGA_Clean(void)
344 {
345     VGA_Exit();
346     VGA_DeinstallTimer();
347 }