Fixed some issues found by winapi_check.
[wine] / dlls / winedos / vga.c
1 /*
2  * VGA hardware emulation
3  *
4  * Copyright 1998 Ove Kåven (with some help from Marcus Meissner)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <string.h>
22 #include "winbase.h"
23 #include "wingdi.h"
24 #include "winuser.h"
25 #include "wincon.h"
26 #include "miscemu.h"
27 #include "dosexe.h"
28 #include "vga.h"
29 #include "ddraw.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
33
34 static IDirectDraw *lpddraw = NULL;
35 static IDirectDrawSurface *lpddsurf;
36 static IDirectDrawPalette *lpddpal;
37 static DDSURFACEDESC sdesc;
38 static LONG vga_refresh;
39 static HANDLE poll_timer;
40
41 /*
42  * VGA controller memory is emulated using linear framebuffer.
43  * This frambuffer also acts as an interface
44  * between VGA controller emulation and DirectDraw.
45  *
46  * vga_fb_width: Display width in pixels. Can be modified when
47  *               display mode is changed.
48  * vga_fb_height: Display height in pixels. Can be modified when
49  *                display mode is changed.
50  * vga_fb_depth: Number of bits used to store single pixel color information.
51  *               Each pixel uses (vga_fb_depth+7)/8 bytes because
52  *               1-16 color modes are mapped to 256 color mode.
53  *               Can be modified when display mode is changed.
54  * vga_fb_pitch: How many bytes to add to pointer in order to move
55  *               from one row to another. This is fixed in VGA modes,
56  *               but can be modified in SVGA modes.
57  * vga_fb_offset: Offset added to framebuffer start address in order
58  *                to find the display origin. Programs use this to do
59  *                double buffering and to scroll display. The value can
60  *                be modified in VGA and SVGA modes.
61  * vga_fb_size: How many bytes are allocated to framebuffer.
62  *              VGA framebuffers are always larger than display size and
63  *              SVGA framebuffers may also be.
64  * vga_fb_data: Pointer to framebuffer start.
65  */
66 static int   vga_fb_width;
67 static int   vga_fb_height;
68 static int   vga_fb_depth;
69 static int   vga_fb_pitch;
70 static int   vga_fb_offset;
71 static int   vga_fb_size = 0;
72 static void *vga_fb_data = 0;
73
74 static BYTE vga_text_attr;
75 static char *textbuf_old = NULL;
76
77 /*
78  * VGA controller ports 0x3c0, 0x3c4, 0x3ce and 0x3d4 are
79  * indexed registers. These ports are used to select VGA controller
80  * subregister that can be written to or read from using ports 0x3c1,
81  * 0x3c5, 0x3cf or 0x3d5. Selected subregister indexes are
82  * stored in variables vga_index_*.
83  *
84  * Port 0x3c0 is special because it is both index and
85  * data-write register. Flip-flop vga_address_3c0 tells whether
86  * the port acts currently as an address register. Reading from port
87  * 0x3da resets the flip-flop to address mode.
88  */
89 static BYTE vga_index_3c0;
90 static BYTE vga_index_3c4;
91 static BYTE vga_index_3ce;
92 static BYTE vga_index_3d4;
93 static BOOL vga_address_3c0 = TRUE;
94
95 static BOOL vga_mode_initialized = FALSE;
96
97 static CRITICAL_SECTION vga_lock = CRITICAL_SECTION_INIT("VGA");
98
99 typedef HRESULT (WINAPI *DirectDrawCreateProc)(LPGUID,LPDIRECTDRAW *,LPUNKNOWN);
100 static DirectDrawCreateProc pDirectDrawCreate;
101
102 static void CALLBACK VGA_Poll( LPVOID arg, DWORD low, DWORD high );
103
104 static HWND vga_hwnd = (HWND) NULL;
105
106 /*
107  * For simplicity, I'm creating a second palette.
108  * 16 color accesses will use these pointers and insert
109  * entries from the 64-color palette into the default
110  * palette.   --Robert 'Admiral' Coeyman
111  */
112
113 static char vga_16_palette[17]={
114   0x00,  /* 0 - Black         */
115   0x01,  /* 1 - Blue          */
116   0x02,  /* 2 - Green         */
117   0x03,  /* 3 - Cyan          */
118   0x04,  /* 4 - Red           */
119   0x05,  /* 5 - Magenta       */
120   0x14,  /* 6 - Brown         */
121   0x07,  /* 7 - Light gray    */
122   0x38,  /* 8 - Dark gray     */
123   0x39,  /* 9 - Light blue    */
124   0x3a,  /* A - Light green   */
125   0x3b,  /* B - Light cyan    */
126   0x3c,  /* C - Light red     */
127   0x3d,  /* D - Light magenta */
128   0x3e,  /* E - Yellow        */
129   0x3f,  /* F - White         */
130   0x00   /* Border Color      */
131 };
132
133 static PALETTEENTRY vga_def_palette[256]={
134 /* red  green  blue */
135   {0x00, 0x00, 0x00}, /* 0 - Black */
136   {0x00, 0x00, 0x80}, /* 1 - Blue */
137   {0x00, 0x80, 0x00}, /* 2 - Green */
138   {0x00, 0x80, 0x80}, /* 3 - Cyan */
139   {0x80, 0x00, 0x00}, /* 4 - Red */
140   {0x80, 0x00, 0x80}, /* 5 - Magenta */
141   {0x80, 0x80, 0x00}, /* 6 - Brown */
142   {0xC0, 0xC0, 0xC0}, /* 7 - Light gray */
143   {0x80, 0x80, 0x80}, /* 8 - Dark gray */
144   {0x00, 0x00, 0xFF}, /* 9 - Light blue */
145   {0x00, 0xFF, 0x00}, /* A - Light green */
146   {0x00, 0xFF, 0xFF}, /* B - Light cyan */
147   {0xFF, 0x00, 0x00}, /* C - Light red */
148   {0xFF, 0x00, 0xFF}, /* D - Light magenta */
149   {0xFF, 0xFF, 0x00}, /* E - Yellow */
150   {0xFF, 0xFF, 0xFF}, /* F - White */
151   {0,0,0} /* FIXME: a series of continuous rainbow hues should follow */
152 };
153
154 /*
155  *   This palette is the dos default, converted from 18 bit color to 24.
156  *      It contains only 64 entries of colors--all others are zeros.
157  *          --Robert 'Admiral' Coeyman
158  */
159 static PALETTEENTRY vga_def64_palette[256]={
160 /* red  green  blue */
161   {0x00, 0x00, 0x00}, /* 0x00      Black      */
162   {0x00, 0x00, 0xaa}, /* 0x01      Blue       */
163   {0x00, 0xaa, 0x00}, /* 0x02      Green      */
164   {0x00, 0xaa, 0xaa}, /* 0x03      Cyan       */
165   {0xaa, 0x00, 0x00}, /* 0x04      Red        */
166   {0xaa, 0x00, 0xaa}, /* 0x05      Magenta    */
167   {0xaa, 0xaa, 0x00}, /* 0x06      */
168   {0xaa, 0xaa, 0xaa}, /* 0x07      Light Gray */
169   {0x00, 0x00, 0x55}, /* 0x08      */
170   {0x00, 0x00, 0xff}, /* 0x09      */
171   {0x00, 0xaa, 0x55}, /* 0x0a      */
172   {0x00, 0xaa, 0xff}, /* 0x0b      */
173   {0xaa, 0x00, 0x55}, /* 0x0c      */
174   {0xaa, 0x00, 0xff}, /* 0x0d      */
175   {0xaa, 0xaa, 0x55}, /* 0x0e      */
176   {0xaa, 0xaa, 0xff}, /* 0x0f      */
177   {0x00, 0x55, 0x00}, /* 0x10      */
178   {0x00, 0x55, 0xaa}, /* 0x11      */
179   {0x00, 0xff, 0x00}, /* 0x12      */
180   {0x00, 0xff, 0xaa}, /* 0x13      */
181   {0xaa, 0x55, 0x00}, /* 0x14      Brown      */
182   {0xaa, 0x55, 0xaa}, /* 0x15      */
183   {0xaa, 0xff, 0x00}, /* 0x16      */
184   {0xaa, 0xff, 0xaa}, /* 0x17      */
185   {0x00, 0x55, 0x55}, /* 0x18      */
186   {0x00, 0x55, 0xff}, /* 0x19      */
187   {0x00, 0xff, 0x55}, /* 0x1a      */
188   {0x00, 0xff, 0xff}, /* 0x1b      */
189   {0xaa, 0x55, 0x55}, /* 0x1c      */
190   {0xaa, 0x55, 0xff}, /* 0x1d      */
191   {0xaa, 0xff, 0x55}, /* 0x1e      */
192   {0xaa, 0xff, 0xff}, /* 0x1f      */
193   {0x55, 0x00, 0x00}, /* 0x20      */
194   {0x55, 0x00, 0xaa}, /* 0x21      */
195   {0x55, 0xaa, 0x00}, /* 0x22      */
196   {0x55, 0xaa, 0xaa}, /* 0x23      */
197   {0xff, 0x00, 0x00}, /* 0x24      */
198   {0xff, 0x00, 0xaa}, /* 0x25      */
199   {0xff, 0xaa, 0x00}, /* 0x26      */
200   {0xff, 0xaa, 0xaa}, /* 0x27      */
201   {0x55, 0x00, 0x55}, /* 0x28      */
202   {0x55, 0x00, 0xff}, /* 0x29      */
203   {0x55, 0xaa, 0x55}, /* 0x2a      */
204   {0x55, 0xaa, 0xff}, /* 0x2b      */
205   {0xff, 0x00, 0x55}, /* 0x2c      */
206   {0xff, 0x00, 0xff}, /* 0x2d      */
207   {0xff, 0xaa, 0x55}, /* 0x2e      */
208   {0xff, 0xaa, 0xff}, /* 0x2f      */
209   {0x55, 0x55, 0x00}, /* 0x30      */
210   {0x55, 0x55, 0xaa}, /* 0x31      */
211   {0x55, 0xff, 0x00}, /* 0x32      */
212   {0x55, 0xff, 0xaa}, /* 0x33      */
213   {0xff, 0x55, 0x00}, /* 0x34      */
214   {0xff, 0x55, 0xaa}, /* 0x35      */
215   {0xff, 0xff, 0x00}, /* 0x36      */
216   {0xff, 0xff, 0xaa}, /* 0x37      */
217   {0x55, 0x55, 0x55}, /* 0x38      Dark Gray     */
218   {0x55, 0x55, 0xff}, /* 0x39      Light Blue    */
219   {0x55, 0xff, 0x55}, /* 0x3a      Light Green   */
220   {0x55, 0xff, 0xff}, /* 0x3b      Light Cyan    */
221   {0xff, 0x55, 0x55}, /* 0x3c      Light Red     */
222   {0xff, 0x55, 0xff}, /* 0x3d      Light Magenta */
223   {0xff, 0xff, 0x55}, /* 0x3e      Yellow        */
224   {0xff, 0xff, 0xff}, /* 0x3f      White         */
225   {0,0,0} /* The next 192 entries are all zeros  */
226 };
227
228 static HANDLE VGA_timer;
229 static HANDLE VGA_timer_thread;
230
231 /* set the timer rate; called in the polling thread context */
232 static void CALLBACK set_timer_rate( ULONG_PTR arg )
233 {
234     LARGE_INTEGER when;
235
236     when.s.LowPart = when.s.HighPart = 0;
237     SetWaitableTimer( VGA_timer, &when, arg, VGA_Poll, 0, FALSE );
238 }
239
240 static DWORD CALLBACK VGA_TimerThread( void *dummy )
241 {
242     for (;;) WaitForMultipleObjectsEx( 0, NULL, FALSE, INFINITE, TRUE );
243 }
244
245 static void VGA_DeinstallTimer(void)
246 {
247     if (VGA_timer_thread)
248     {
249         CancelWaitableTimer( VGA_timer );
250         CloseHandle( VGA_timer );
251         TerminateThread( VGA_timer_thread, 0 );
252         CloseHandle( VGA_timer_thread );
253         VGA_timer_thread = 0;
254     }
255 }
256
257 static void VGA_InstallTimer(unsigned Rate)
258 {
259     if (!VGA_timer_thread)
260     {
261         VGA_timer = CreateWaitableTimerA( NULL, FALSE, NULL );
262         VGA_timer_thread = CreateThread( NULL, 0, VGA_TimerThread, NULL, 0, NULL );
263     }
264     QueueUserAPC( set_timer_rate, VGA_timer_thread, (ULONG_PTR)Rate );
265 }
266
267 HANDLE VGA_AlphaConsole(void)
268 {
269     /* this assumes that no Win32 redirection has taken place, but then again,
270      * only 16-bit apps are likely to use this part of Wine... */
271     return GetStdHandle(STD_OUTPUT_HANDLE);
272 }
273
274 char*VGA_AlphaBuffer(void)
275 {
276     return DOSMEM_MapDosToLinear(0xb8000);
277 }
278
279 /*** GRAPHICS MODE ***/
280
281 typedef struct {
282   unsigned Xres, Yres, Depth;
283   int ret;
284 } ModeSet;
285
286 static void WINAPI VGA_DoExit(ULONG_PTR arg)
287 {
288     VGA_DeinstallTimer();
289     IDirectDrawSurface_SetPalette(lpddsurf,NULL);
290     IDirectDrawSurface_Release(lpddsurf);
291     lpddsurf=NULL;
292     IDirectDrawPalette_Release(lpddpal);
293     lpddpal=NULL;
294     IDirectDraw_Release(lpddraw);
295     lpddraw=NULL;
296 }
297
298 static void WINAPI VGA_DoSetMode(ULONG_PTR arg)
299 {
300     LRESULT     res;
301     ModeSet *par = (ModeSet *)arg;
302     par->ret=1;
303
304     if (lpddraw) VGA_DoExit(0);
305     if (!lpddraw) {
306         if (!pDirectDrawCreate)
307         {
308             HMODULE hmod = LoadLibraryA( "ddraw.dll" );
309             if (hmod) pDirectDrawCreate = (DirectDrawCreateProc)GetProcAddress( hmod, "DirectDrawCreate" );
310             if (!pDirectDrawCreate) {
311                 ERR("Can't lookup DirectDrawCreate from ddraw.dll.\n");
312                 return;
313             }
314         }
315         res = pDirectDrawCreate(NULL,&lpddraw,NULL);
316         if (!lpddraw) {
317             ERR("DirectDraw is not available (res = %lx)\n",res);
318             return;
319         }
320         if (!vga_hwnd) {
321             vga_hwnd = CreateWindowExA(0,"STATIC","WINEDOS VGA",WS_POPUP|WS_VISIBLE,0,0,par->Xres,par->Yres,0,0,0,NULL);
322             if (!vga_hwnd) {
323                 ERR("Failed to create user window.\n");
324                 IDirectDraw_Release(lpddraw);
325                 lpddraw=NULL;
326                 return;
327             }
328         }
329         else
330             SetWindowPos(vga_hwnd,0,0,0,par->Xres,par->Yres,SWP_NOMOVE|SWP_NOZORDER);
331
332         if ((res=IDirectDraw_SetCooperativeLevel(lpddraw,vga_hwnd,DDSCL_FULLSCREEN|DDSCL_EXCLUSIVE))) {
333             ERR("Could not set cooperative level to exclusive (%lx)\n",res);
334         }
335
336         if ((res=IDirectDraw_SetDisplayMode(lpddraw,par->Xres,par->Yres,par->Depth))) {
337             ERR("DirectDraw does not support requested display mode (%dx%dx%d), res = %lx!\n",par->Xres,par->Yres,par->Depth,res);
338             IDirectDraw_Release(lpddraw);
339             lpddraw=NULL;
340             return;
341         }
342
343         res=IDirectDraw_CreatePalette(lpddraw,DDPCAPS_8BIT,NULL,&lpddpal,NULL);
344         if (res) {
345             ERR("Could not create palette (res = %lx)\n",res);
346             IDirectDraw_Release(lpddraw);
347             lpddraw=NULL;
348             return;
349         }
350         if ((res=IDirectDrawPalette_SetEntries(lpddpal,0,0,256,vga_def_palette))) {
351             ERR("Could not set default palette entries (res = %lx)\n", res);
352         }
353
354         memset(&sdesc,0,sizeof(sdesc));
355         sdesc.dwSize=sizeof(sdesc);
356         sdesc.dwFlags = DDSD_CAPS;
357         sdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
358         if (IDirectDraw_CreateSurface(lpddraw,&sdesc,&lpddsurf,NULL)||(!lpddsurf)) {
359             ERR("DirectDraw surface is not available\n");
360             IDirectDraw_Release(lpddraw);
361             lpddraw=NULL;
362             return;
363         }
364         IDirectDrawSurface_SetPalette(lpddsurf,lpddpal);
365         vga_refresh=0;
366         /* poll every 20ms (50fps should provide adequate responsiveness) */
367         VGA_InstallTimer(20);
368     }
369     par->ret=0;
370     return;
371 }
372
373 int VGA_SetMode(unsigned Xres,unsigned Yres,unsigned Depth)
374 {
375     ModeSet par;
376     int     newSize;
377
378     vga_fb_width = Xres;
379     vga_fb_height = Yres;
380     vga_fb_depth = Depth;
381     vga_fb_offset = 0;
382     vga_fb_pitch = Xres * ((Depth + 7) / 8);
383
384     newSize = Xres * Yres * ((Depth + 7) / 8);
385     if(newSize < 256 * 1024)
386       newSize = 256 * 1024;
387
388     if(vga_fb_size < newSize) {
389       if(vga_fb_data)
390         HeapFree(GetProcessHeap(), 0, vga_fb_data);
391       vga_fb_data = HeapAlloc(GetProcessHeap(), 0, newSize);
392       vga_fb_size = newSize;
393     }
394
395     if(Xres >= 640 || Yres >= 480) {
396       par.Xres = Xres;
397       par.Yres = Yres;
398     } else {
399       par.Xres = 640;
400       par.Yres = 480;
401     }
402
403     par.Depth = (Depth < 8) ? 8 : Depth;
404
405     vga_mode_initialized = TRUE;
406
407     MZ_RunInThread(VGA_DoSetMode, (ULONG_PTR)&par);
408     return par.ret;
409 }
410
411 int VGA_GetMode(unsigned*Height,unsigned*Width,unsigned*Depth)
412 {
413     if (!lpddraw) return 1;
414     if (!lpddsurf) return 1;
415     if (Height) *Height=sdesc.dwHeight;
416     if (Width) *Width=sdesc.dwWidth;
417     if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
418     return 0;
419 }
420
421 void VGA_Exit(void)
422 {
423     if (lpddraw) MZ_RunInThread(VGA_DoExit, 0);
424 }
425
426 void VGA_SetPalette(PALETTEENTRY*pal,int start,int len)
427 {
428     if (!lpddraw) return;
429     IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
430 }
431
432 /* set a single [char wide] color in 16 color mode. */
433 void VGA_SetColor16(int reg,int color)
434 {
435         PALETTEENTRY *pal;
436
437     if (!lpddraw) return;
438         pal= &vga_def64_palette[color];
439         IDirectDrawPalette_SetEntries(lpddpal,0,reg,1,pal);
440         vga_16_palette[reg]=(char)color;
441 }
442
443 /* Get a single [char wide] color in 16 color mode. */
444 char VGA_GetColor16(int reg)
445 {
446
447     if (!lpddraw) return 0;
448         return (char)vga_16_palette[reg];
449 }
450
451 /* set all 17 [char wide] colors at once in 16 color mode. */
452 void VGA_Set16Palette(char *Table)
453 {
454         PALETTEENTRY *pal;
455         int c;
456
457     if (!lpddraw) return;         /* return if we're in text only mode */
458         bcopy((void *)&vga_16_palette,(void *)Table,17);
459                                     /* copy the entries into the table */
460     for (c=0; c<17; c++) {                                /* 17 entries */
461         pal= &vga_def64_palette[(int)vga_16_palette[c]];  /* get color  */
462         IDirectDrawPalette_SetEntries(lpddpal,0,c,1,pal); /* set entry  */
463         TRACE("Palette register %d set to %d\n",c,(int)vga_16_palette[c]);
464    } /* end of the counting loop */
465 }
466
467 /* Get all 17 [ char wide ] colors at once in 16 color mode. */
468 void VGA_Get16Palette(char *Table)
469 {
470
471     if (!lpddraw) return;         /* return if we're in text only mode */
472         bcopy((void *)Table,(void *)&vga_16_palette,17);
473                                     /* copy the entries into the table */
474 }
475
476 void VGA_SetQuadPalette(RGBQUAD*color,int start,int len)
477 {
478     PALETTEENTRY pal[256];
479     int c;
480
481     if (!lpddraw) return;
482     for (c=0; c<len; c++) {
483         pal[c].peRed  =color[c].rgbRed;
484         pal[c].peGreen=color[c].rgbGreen;
485         pal[c].peBlue =color[c].rgbBlue;
486         pal[c].peFlags=0;
487     }
488     IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
489 }
490
491 LPSTR VGA_Lock(unsigned*Pitch,unsigned*Height,unsigned*Width,unsigned*Depth)
492 {
493     if (!lpddraw) return NULL;
494     if (!lpddsurf) return NULL;
495     if (IDirectDrawSurface_Lock(lpddsurf,NULL,&sdesc,0,0)) {
496         ERR("could not lock surface!\n");
497         return NULL;
498     }
499     if (Pitch) *Pitch=sdesc.u1.lPitch;
500     if (Height) *Height=sdesc.dwHeight;
501     if (Width) *Width=sdesc.dwWidth;
502     if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
503     return sdesc.lpSurface;
504 }
505
506 void VGA_Unlock(void)
507 {
508     IDirectDrawSurface_Unlock(lpddsurf,sdesc.lpSurface);
509 }
510
511 /*** TEXT MODE ***/
512
513 /* prepare the text mode video memory copy that is used to only
514  * update the video memory line that did get updated. */
515 void VGA_PrepareVideoMemCopy(unsigned Xres, unsigned Yres)
516 {
517     char *p, *p2;
518     int i;
519
520     textbuf_old = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textbuf_old, Xres * Yres * 2); /* char + attr */
521
522     p = VGA_AlphaBuffer();
523     p2 = textbuf_old;
524
525     /* make sure the video mem copy contains the exact opposite of our
526      * actual text mode memory area to make sure the screen
527      * does get updated fully initially */
528     for (i=0; i < Xres*Yres*2; i++)
529         *p2++ ^= *p++; /* XOR it */
530 }
531
532 int VGA_SetAlphaMode(unsigned Xres,unsigned Yres)
533 {
534     COORD siz;
535
536     if (lpddraw) VGA_Exit();
537
538     /* FIXME: Where to initialize text attributes? */
539     VGA_SetTextAttribute(0xf);
540
541     VGA_PrepareVideoMemCopy(Xres, Yres);
542
543     /* poll every 30ms (33fps should provide adequate responsiveness) */
544     VGA_InstallTimer(30);
545
546     siz.X = Xres;
547     siz.Y = Yres;
548     SetConsoleScreenBufferSize(VGA_AlphaConsole(),siz);
549     return 0;
550 }
551
552 BOOL VGA_GetAlphaMode(unsigned*Xres,unsigned*Yres)
553 {
554     CONSOLE_SCREEN_BUFFER_INFO info;
555     if(!GetConsoleScreenBufferInfo(VGA_AlphaConsole(),&info))
556     {
557         return FALSE;
558     } else {
559         if (Xres) *Xres=info.dwSize.X;
560         if (Yres) *Yres=info.dwSize.Y;
561         return TRUE;
562     }
563 }
564
565 void VGA_SetCursorShape(unsigned char start_options, unsigned char end)
566 {
567     CONSOLE_CURSOR_INFO cci;
568
569     /* standard cursor settings:
570      * 0x0607 == CGA, 0x0b0c == monochrome, 0x0d0e == EGA/VGA */
571
572     /* calculate percentage from bottom - assuming VGA (bottom 0x0e) */
573     cci.dwSize = ((end & 0x1f) - (start_options & 0x1f))/0x0e * 100;
574     if (!cci.dwSize) cci.dwSize++; /* NULL cursor would make SCCI() fail ! */
575     cci.bVisible = ((start_options & 0x60) != 0x20); /* invisible ? */
576
577     SetConsoleCursorInfo(VGA_AlphaConsole(),&cci);
578 }
579
580 void VGA_SetCursorPos(unsigned X,unsigned Y)
581 {
582     COORD pos;
583
584     if (!poll_timer) VGA_SetAlphaMode(80, 25);
585     pos.X = X;
586     pos.Y = Y;
587     SetConsoleCursorPosition(VGA_AlphaConsole(),pos);
588 }
589
590 void VGA_GetCursorPos(unsigned*X,unsigned*Y)
591 {
592     CONSOLE_SCREEN_BUFFER_INFO info;
593     GetConsoleScreenBufferInfo(VGA_AlphaConsole(),&info);
594     if (X) *X=info.dwCursorPosition.X;
595     if (Y) *Y=info.dwCursorPosition.Y;
596 }
597
598 void VGA_WriteChars(unsigned X,unsigned Y,unsigned ch,int attr,int count)
599 {
600     CHAR_INFO info;
601     COORD siz, off;
602     SMALL_RECT dest;
603     unsigned XR, YR;
604     char *dat;
605
606     EnterCriticalSection(&vga_lock);
607
608     info.Char.AsciiChar = ch;
609     info.Attributes = (WORD)attr;
610     siz.X = 1;
611     siz.Y = 1;
612     off.X = 0;
613     off.Y = 0;
614     dest.Top=Y;
615     dest.Bottom=Y;
616
617     VGA_GetAlphaMode(&XR, &YR);
618     dat = VGA_AlphaBuffer() + ((XR*Y + X) * 2);
619     while (count--) {
620         dest.Left = X + count;
621        dest.Right = X + count;
622
623         *dat++ = ch;
624         if (attr>=0)
625          *dat = attr;
626        else
627          info.Attributes = *dat;
628         dat++;
629
630        WriteConsoleOutputA(VGA_AlphaConsole(), &info, siz, off, &dest);
631     }
632
633     LeaveCriticalSection(&vga_lock);
634 }
635
636 static void VGA_PutCharAt(BYTE ascii, unsigned x, unsigned y)
637 {
638     unsigned width, height;
639     char *dat;
640
641     VGA_GetAlphaMode(&width, &height);
642     dat = VGA_AlphaBuffer() + ((width*y + x) * 2);
643     dat[0] = ascii;
644     dat[1] = vga_text_attr;
645 }
646
647 void VGA_PutChar(BYTE ascii)
648 {
649     unsigned width, height, x, y, nx, ny;
650
651     EnterCriticalSection(&vga_lock);
652
653     VGA_GetAlphaMode(&width, &height);
654     VGA_GetCursorPos(&x, &y);
655
656     switch(ascii) {
657     case '\b':
658         VGA_PutCharAt(' ', x, y);
659        x--;
660        break;
661
662     case '\t':
663        x += ((x + 8) & ~7) - x;
664        break;
665
666     case '\n':
667         y++;
668        x = 0;
669        break;
670
671     case '\a':
672         break;
673
674     case '\r':
675         x = 0;
676        break;
677
678     default:
679         VGA_PutCharAt(ascii, x, y);
680        x++;
681     }
682
683     /*
684      * FIXME: add line wrapping and scrolling
685      */
686
687     WriteFile(VGA_AlphaConsole(), &ascii, 1, NULL, NULL);
688
689     /*
690      * The following is just a sanity check.
691      */
692     VGA_GetCursorPos(&nx, &ny);
693     if(nx != x || ny != y)
694       WARN("VGA emulator and text console have become unsynchronized.\n");
695
696     LeaveCriticalSection(&vga_lock);
697 }
698
699 void VGA_SetTextAttribute(BYTE attr)
700 {
701     vga_text_attr = attr;
702     SetConsoleTextAttribute(VGA_AlphaConsole(), attr);
703 }
704
705 BOOL VGA_ClearText(unsigned row1, unsigned col1,
706                   unsigned row2, unsigned col2,
707                   BYTE attr)
708 {
709     unsigned width, height, x, y;
710     COORD off;
711     char *dat = VGA_AlphaBuffer();
712     HANDLE con = VGA_AlphaConsole();
713
714     /* return if we fail to get the height and width of the window */
715     if(!VGA_GetAlphaMode(&width, &height))
716     {
717         ERR("failed\n");
718         return FALSE;
719     }
720
721     TRACE("dat = %p, width = %d, height = %d\n", dat, width, height);
722
723     EnterCriticalSection(&vga_lock);
724
725     for(y=row1; y<=row2; y++) {
726         off.X = col1;
727        off.Y = y;
728        FillConsoleOutputCharacterA(con, ' ', col2-col1+1, off, NULL);
729        FillConsoleOutputAttribute(con, attr, col2-col1+1, off, NULL);
730
731        for(x=col1; x<=col2; x++) {
732            char *ptr = dat + ((width*y + x) * 2);
733            ptr[0] = ' ';
734            ptr[1] = attr;
735        }
736     }
737
738     LeaveCriticalSection(&vga_lock);
739
740     return TRUE;
741 }
742
743 void VGA_ScrollUpText(unsigned row1, unsigned col1,
744                      unsigned row2, unsigned col2,
745                      unsigned lines, BYTE attr)
746 {
747     FIXME("not implemented\n");
748 }
749
750 void VGA_ScrollDownText(unsigned row1, unsigned col1,
751                        unsigned row2, unsigned col2,
752                        unsigned lines, BYTE attr)
753 {
754     FIXME("not implemented\n");
755 }
756
757 void VGA_GetCharacterAtCursor(BYTE *ascii, BYTE *attr)
758 {
759     unsigned width, height, x, y;
760     char *dat;
761
762     VGA_GetAlphaMode(&width, &height);
763     VGA_GetCursorPos(&x, &y);
764     dat = VGA_AlphaBuffer() + ((width*y + x) * 2);
765
766     *ascii = dat[0];
767     *attr = dat[1];
768 }
769
770
771 /*** CONTROL ***/
772
773 /*
774  * Copy part of VGA framebuffer to VGA window.
775  */
776 static void VGA_CopyFrameToWindow(void)
777 {
778   /* FIXME: add implementation */
779 }
780
781 /*
782  * Copy contents of VGA window to VGA framebuffer.
783  */
784 static void VGA_CopyWindowToFrame(void)
785 {
786   /* FIXME: fix implementation */
787   char *dat = DOSMEM_MapDosToLinear(0xa0000);
788   memmove(vga_fb_data, dat, 65536);
789 }
790
791 /* FIXME: optimize by doing this only if the data has actually changed
792  *        (in a way similar to DIBSection, perhaps) */
793 static void VGA_Poll_Graphics(void)
794 {
795   unsigned int Pitch, Height, Width, X, Y;
796   char *surf;
797   char *dat = vga_fb_data + vga_fb_offset;
798   int   bpp = (vga_fb_depth + 7) / 8;
799
800   surf = VGA_Lock(&Pitch,&Height,&Width,NULL);
801   if (!surf) return;
802
803   /*
804    * Synchronize framebuffer contents.
805    */
806   VGA_CopyWindowToFrame();
807
808   /*
809    * Double VGA framebuffer (320x200 -> 640x400), if needed.
810    */
811   if(Height >= 2 * vga_fb_height && Width >= 2 * vga_fb_width && bpp == 1)
812     for (Y=0; Y<vga_fb_height; Y++,surf+=Pitch*2,dat+=vga_fb_pitch)
813       for (X=0; X<vga_fb_width; X++) {
814        BYTE value = dat[X];
815        surf[X*2] = value;
816        surf[X*2+1] = value;
817        surf[X*2+Pitch] = value;
818        surf[X*2+Pitch+1] = value;
819       }
820   else
821     for (Y=0; Y<vga_fb_height; Y++,surf+=Pitch,dat+=vga_fb_pitch)
822       memcpy(surf, dat, vga_fb_width * bpp);
823
824   VGA_Unlock();
825 }
826
827 static void VGA_Poll_Text(void)
828 {
829     char *dat, *old, *p_line;
830     unsigned int Height,Width,Y,X;
831     CHAR_INFO ch[256]; /* that should suffice for the largest text width */
832     COORD siz, off;
833     SMALL_RECT dest;
834     HANDLE con = VGA_AlphaConsole();
835     BOOL linechanged = FALSE; /* video memory area differs from stored copy ? */
836
837     VGA_GetAlphaMode(&Width,&Height);
838     dat = VGA_AlphaBuffer();
839     old = textbuf_old; /* pointer to stored video mem copy */
840     siz.X = Width; siz.Y = 1;
841     off.X = 0; off.Y = 0;
842     /* copy from virtual VGA frame buffer to console */
843     for (Y=0; Y<Height; Y++) {
844         linechanged = memcmp(dat, old, Width*2);
845         if (linechanged)
846         {
847             /*TRACE("line %d changed\n", Y);*/
848             p_line = dat;
849             for (X=0; X<Width; X++) {
850                 ch[X].Char.AsciiChar = *p_line++;
851                 /* WriteConsoleOutputA doesn't like "dead" chars */
852                 if (ch[X].Char.AsciiChar == '\0')
853                     ch[X].Char.AsciiChar = ' ';
854                 ch[X].Attributes = *p_line++;
855             }
856             dest.Top=Y; dest.Bottom=Y;
857             dest.Left=0; dest.Right=Width+1;
858             WriteConsoleOutputA(con, ch, siz, off, &dest);
859             memcpy(old, dat, Width*2);
860         }
861         /* advance to next text line */
862         dat += Width*2;
863         old += Width*2;
864     }
865 }
866
867 static void CALLBACK VGA_Poll( LPVOID arg, DWORD low, DWORD high )
868 {
869     if(!TryEnterCriticalSection(&vga_lock))
870         return;
871
872     if (lpddraw) {
873         VGA_Poll_Graphics();
874     } else {
875         VGA_Poll_Text();
876     }
877
878     vga_refresh=1;
879     LeaveCriticalSection(&vga_lock);
880 }
881
882 static BYTE palreg,palcnt;
883 static PALETTEENTRY paldat;
884
885 void VGA_ioport_out( WORD port, BYTE val )
886 {
887     switch (port) {
888         case 0x3c0:
889            if (vga_address_3c0)
890                vga_index_3c0 = val;
891            else
892                FIXME("Unsupported index, register 0x3c0: 0x%02x (value 0x%02x)\n",
893                      vga_index_3c0, val);
894            vga_address_3c0 = !vga_address_3c0;
895            break;
896         case 0x3c4:
897            vga_index_3c4 = val;
898            break;
899         case 0x3c5:
900            FIXME("Unsupported index, register 0x3c4: 0x%02x (value 0x%02x)\n",
901                  vga_index_3c4, val);
902            break;
903         case 0x3c8:
904             palreg=val; palcnt=0; break;
905         case 0x3c9:
906             ((BYTE*)&paldat)[palcnt++]=val << 2;
907             if (palcnt==3) {
908                 VGA_SetPalette(&paldat,palreg++,1);
909                 palcnt=0;
910             }
911             break;
912         case 0x3ce:
913             vga_index_3ce = val;
914            break;
915         case 0x3cf:
916            FIXME("Unsupported index, register 0x3ce: 0x%02x (value 0x%02x)\n",
917                  vga_index_3ce, val);
918            break;
919         case 0x3d4:
920            vga_index_3d4 = val;
921            break;
922         case 0x3d5:
923            FIXME("Unsupported index, register 0x3d4: 0x%02x (value 0x%02x)\n",
924                  vga_index_3d4, val);
925            break;
926         default:
927             FIXME("Unsupported VGA register: 0x%04x (value 0x%02x)\n", port, val);
928     }
929 }
930
931 BYTE VGA_ioport_in( WORD port )
932 {
933     BYTE ret;
934
935     switch (port) {
936         case 0x3c1:
937            FIXME("Unsupported index, register 0x3c0: 0x%02x\n",
938                  vga_index_3c0);
939            return 0xff;
940         case 0x3c5:
941             FIXME("Unsupported index, register 0x3c4: 0x%02x\n",
942                  vga_index_3c4);
943            return 0xff;
944         case 0x3cf:
945            FIXME("Unsupported index, register 0x3ce: 0x%02x\n",
946                  vga_index_3ce);
947            return 0xff;
948         case 0x3d5:
949            FIXME("Unsupported index, register 0x3d4: 0x%02x\n",
950                  vga_index_3d4);
951            return 0xff;
952         case 0x3da:
953            /*
954             * Read from this register resets register 0x3c0 address flip-flop.
955             */
956            vga_address_3c0 = TRUE;
957             /* since we don't (yet?) serve DOS VM requests while VGA_Poll is running,
958                we need to fake the occurrence of the vertical refresh */
959             ret=vga_refresh?0x00:0x0b; /* toggle video RAM and lightpen and VGA refresh bits ! */
960             if (vga_mode_initialized)
961                 vga_refresh=0;
962             else
963                 /* Also fake the occurence of the vertical refresh when no graphic
964                    mode has been set */
965                 vga_refresh=!vga_refresh;
966             break;
967         default:
968             ret=0xff;
969             FIXME("Unsupported VGA register: 0x%04x\n", port);
970     }
971     return ret;
972 }
973
974 void VGA_Clean(void)
975 {
976     VGA_Exit();
977     VGA_DeinstallTimer();
978 }