FreeBASIC  0.91.0
gfx_driver_ddraw.c
Go to the documentation of this file.
1 /* DirectDraw gfx driver */
2 
3 #include "../fb_gfx.h"
4 #include "fb_gfx_win32.h"
5 
6 #ifndef HOST_CYGWIN
7 
8 #include <objbase.h>
9 #include <ddraw.h>
10 #include <dinput.h>
11 
12 /* Normally these globals are found in -ldxguid, but we don't link that into
13  FB programs because MinGW's libdxguid.a contains lots of other such globals
14  and all of them in one object resulting in lots of unnecessary bloat.
15  So we declare these manually here. They must be renamed from the originals
16  too to prevent collisions with the DirectX/MinGW[-w64] headers. */
17 #define DX_GUID(id,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID id = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
18 DX_GUID( __fb_IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 );
19 DX_GUID( __fb_GUID_Key, 0x55728220,0xD33C,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00 );
20 DX_GUID( __fb_GUID_SysKeyboard, 0x6F1D2B61,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00 );
21 
22 static int driver_init(char *title, int w, int h, int depth, int refresh_rate, int flags);
23 static void driver_wait_vsync(void);
24 static int *driver_fetch_modes(int depth, int *size);
25 
27 {
28  "DirectX", /* char *name; */
29  driver_init, /* int (*init)(int w, int h, char *title, int fullscreen); */
30  fb_hWin32Exit, /* void (*exit)(void); */
31  fb_hWin32Lock, /* void (*lock)(void); */
32  fb_hWin32Unlock, /* void (*unlock)(void); */
33  fb_hWin32SetPalette, /* void (*set_palette)(int index, int r, int g, int b); */
34  driver_wait_vsync, /* void (*wait_vsync)(void); */
35  fb_hWin32GetMouse, /* int (*get_mouse)(int *x, int *y, int *z, int *buttons, int *clip); */
36  fb_hWin32SetMouse, /* void (*set_mouse)(int x, int y, int cursor, int clip); */
37  fb_hWin32SetWindowTitle,/* void (*set_window_title)(char *title); */
38  fb_hWin32SetWindowPos, /* int (*set_window_pos)(int x, int y); */
39  driver_fetch_modes, /* int *(*fetch_modes)(int depth, int *size); */
40  NULL, /* void (*flip)(void); */
41  NULL /* void (*poll_events)(void); */
42 };
43 
44 typedef struct MODESLIST {
45  int depth;
46  int size;
47  int *data;
48 } MODESLIST;
49 
50 typedef struct DEVENUMDATA {
51  GUID guid;
53 } DEVENUMDATA;
54 
55 static int directx_init(void);
56 static void directx_exit(void);
57 
58 /* We don't want to directly link with DDRAW.DLL and DINPUT.DLL,
59  * as this way generated exes will not depend on them to run.
60  * This will ensure if DirectX is not installed in the system,
61  * the programs will still run, fallbacking on the GDI driver.
62  */
63 typedef HRESULT (WINAPI *DIRECTDRAWCREATE)(GUID FAR *lpGUID,LPDIRECTDRAW FAR *lplpDD,IUnknown FAR *pUnkOuter);
64 typedef HRESULT (WINAPI *DIRECTINPUTCREATE)(HINSTANCE hinst,DWORD dwVersion,LPDIRECTINPUT *lplpDI,LPUNKNOWN pUnkOuter);
65 typedef HRESULT (WINAPI *DIRECTDRAWENUMERATEEX)(LPDDENUMCALLBACKEX lpCallback,LPVOID lpContext,DWORD dwFlags);
66 
67 /* Unfortunately c_dfDIKeyboard is a required global variable
68  * defined in import library LIBDINPUT.A, and as we're not
69  * linking with it, we need to define it here...
70  */
71 static DIOBJECTDATAFORMAT __c_rgodfDIKeyboard[256];
72 static const DIDATAFORMAT __c_dfDIKeyboard = { 24, 16, 0x2, 256, 256, __c_rgodfDIKeyboard };
73 static HMODULE dd_library;
74 static HMODULE di_library;
75 static LPDIRECTDRAW2 lpDD = NULL;
76 static LPDIRECTDRAWSURFACE lpDDS;
77 static LPDIRECTDRAWSURFACE lpDDS_back;
78 static LPDIRECTDRAWPALETTE lpDDP;
79 static LPDIRECTINPUT lpDI;
80 static LPDIRECTINPUTDEVICE lpDID;
81 static RECT rect;
84 
85 static void restore_surfaces(void)
86 {
87  HRESULT result;
88  FB_FLASHWINFO fwinfo;
89 
90  result = IDirectDrawSurface_IsLost(lpDDS);
91  while (result == DDERR_SURFACELOST) {
92  if (lpDDS_back != lpDDS)
93  IDirectDrawSurface_Restore(lpDDS_back);
94  result = IDirectDrawSurface_Restore(lpDDS);
95  if (result == DDERR_WRONGMODE) {
96  /* it sucks, we have to recreate all DD objects */
97  directx_exit();
98  while (directx_init()) {
99  directx_exit();
100  Sleep(300);
101  }
102 
103  if (fb_win32.FlashWindowEx) {
104  /* stop our window to flash */
105  fwinfo.cbSize = sizeof(fwinfo);
106  fwinfo.hwnd = fb_win32.wnd;
107  fwinfo.dwFlags = 0;
108  fb_win32.FlashWindowEx(&fwinfo);
109  }
110  }
111  result = IDirectDrawSurface_IsLost(lpDDS);
112  }
113 }
114 
115 static void directx_paint(void)
116 {
117  RECT src, dest;
118  POINT point;
119  HRESULT result;
120 
122  return;
123 
124  src.left = src.top = 0;
125  src.right = fb_win32.w;
126  src.bottom = fb_win32.h;
127  point.x = point.y = 0;
128  ClientToScreen(fb_win32.wnd, &point);
129  GetClientRect(fb_win32.wnd, &dest);
130  dest.left += point.x;
131  dest.top += point.y;
132  dest.right += point.x;
133  dest.bottom += point.y;
134  do {
136  result = IDirectDrawSurface_Blt(lpDDS, &dest, lpDDS_back, &src, DDBLT_WAIT, NULL);
137  } while (result == DDERR_SURFACELOST);
138 }
139 
140 static int calc_comp_height( int h )
141 {
142  if( h < 240 )
143  return 240;
144  else if( h < 480 )
145  return 480;
146  else if( h < 600 )
147  return 600;
148  else if( h < 768 )
149  return 768;
150  else if( h < 1024 )
151  return 1024;
152  else
153  return 0;
154 }
155 
156 static BOOL WINAPI ddenum_callback(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm)
157 {
158  if (hm == fb_win32.monitor && lpGUID) {
159  ((DEVENUMDATA *)lpContext)->guid = *lpGUID;
160  ((DEVENUMDATA *)lpContext)->success = TRUE;
161  return 0;
162  } else {
163  return 1;
164  }
165 }
166 
167 static int directx_init(void)
168 {
169  LPDIRECTDRAW lpDD1 = NULL;
170  LPDIRECTDRAWCLIPPER lpDDC = NULL;
171  GUID *ddGUID = NULL;
172  DIRECTDRAWCREATE DirectDrawCreate;
173  DIRECTDRAWENUMERATEEX DirectDrawEnumerateEx;
174  DIRECTINPUTCREATE DirectInputCreate;
175  DDSURFACEDESC desc;
176  DDPIXELFORMAT format;
177  HRESULT res;
178  DWORD style;
179  int i, depth, is_rgb = FALSE, height, flags;
180  DEVENUMDATA dev_enum_data;
181 
182  lpDD = NULL;
183  lpDDS = NULL;
184  lpDDS_back = NULL;
185  lpDDP = NULL;
186  lpDI = NULL;
187  lpDID = NULL;
188 
189  dd_library = (HMODULE)LoadLibrary("ddraw.dll");
190  if (!dd_library)
191  return -1;
192  di_library = (HMODULE)LoadLibrary("dinput.dll");
193  if (!di_library)
194  return -1;
195 
196  DirectDrawCreate = (DIRECTDRAWCREATE)GetProcAddress(dd_library, "DirectDrawCreate");
197  DirectDrawEnumerateEx = (DIRECTDRAWENUMERATEEX)GetProcAddress(dd_library, "DirectDrawEnumerateExA");
198  DirectInputCreate = (DIRECTINPUTCREATE)GetProcAddress(di_library, "DirectInputCreateA");
199 
200  dev_enum_data.success = FALSE;
201 
202  if (!(fb_win32.flags & DRIVER_FULLSCREEN) || (fb_win32.monitor == NULL) || !DirectDrawEnumerateEx ||
203  (DirectDrawEnumerateEx(ddenum_callback, &dev_enum_data, DDENUM_ATTACHEDSECONDARYDEVICES) != DD_OK) ||
204  !dev_enum_data.success ) {
205  ddGUID = NULL;
206  } else {
207  ddGUID = &dev_enum_data.guid;
208  }
209 
210  if ((!DirectDrawCreate) || (DirectDrawCreate(ddGUID, &lpDD1, NULL) != DD_OK))
211  return -1;
212  res = IDirectDraw_QueryInterface(lpDD1, &__fb_IID_IDirectDraw2, (LPVOID)&lpDD);
213  IDirectDraw_Release(lpDD1);
214  if (res != DD_OK)
215  return -1;
216  if ((!DirectInputCreate) || (DirectInputCreate(fb_win32.hinstance, 0x0300, &lpDI, NULL) != DI_OK))
217  return -1;
218 
219  rect.left = rect.top = 0;
220  rect.right = fb_win32.w;
221  rect.bottom = fb_win32.h;
222 
224  if (fb_hInitWindow(WS_POPUP | WS_VISIBLE, 0, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)))
225  return -1;
226  if (IDirectDraw2_SetCooperativeLevel(lpDD, fb_win32.wnd, DDSCL_ALLOWREBOOT | DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE) != DD_OK)
227  return -1;
228 
229  height = fb_win32.h;
230  while( 1 )
231  {
232  flags = ((fb_win32.w == 320) && (height == 200) && (fb_win32.depth == 8)) ? DDSDM_STANDARDVGAMODE : 0;
233 
234  if (IDirectDraw2_SetDisplayMode(lpDD, fb_win32.w, height, fb_win32.depth, fb_win32.refresh_rate, flags) == DD_OK)
235  break;
236 
237  depth = fb_win32.depth;
238  switch (fb_win32.depth)
239  {
240  case 15: depth = 16; break;
241  case 16: depth = 15; break;
242  case 24: depth = 32; break;
243  case 32: depth = 24; break;
244  }
245 
246  if ((depth == fb_win32.depth) || (IDirectDraw2_SetDisplayMode(lpDD, fb_win32.w, height, depth, fb_win32.refresh_rate, flags) != DD_OK))
247  {
248  height = calc_comp_height( height );
249  if( height == 0 )
250  return -1;
251  } else {
252  break;
253  }
254  }
255  display_offset = ((height - fb_win32.h) >> 1);
256  } else {
258  style = WS_POPUP | WS_VISIBLE;
259  } else {
260  style = (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME) | WS_VISIBLE;
262  style &= ~WS_MAXIMIZEBOX;
263  }
264  AdjustWindowRect(&rect, style, 0);
265  rect.right -= rect.left;
266  rect.bottom -= rect.top;
267  if (fb_hInitWindow(style, 0, win_x, win_y, rect.right, rect.bottom))
268  return -1;
269  if (IDirectDraw2_SetCooperativeLevel(lpDD, fb_win32.wnd, DDSCL_NORMAL) != DD_OK)
270  return -1;
271  if (IDirectDraw2_CreateClipper(lpDD, 0, &lpDDC, NULL) != DD_OK)
272  return -1;
273  if (IDirectDrawClipper_SetHWnd(lpDDC, 0, fb_win32.wnd) != DD_OK)
274  return -1;
275  display_offset = 0;
276  }
277 
278  fb_hMemSet(&desc, 0, sizeof(DDSURFACEDESC));
279  desc.dwSize = sizeof(desc);
280  desc.dwFlags = DDSD_CAPS;
281  desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
282  if (IDirectDraw2_CreateSurface(lpDD, &desc, &lpDDS, NULL) != DD_OK)
283  return -1;
284 
285  if (!(fb_win32.flags & DRIVER_FULLSCREEN)) {
286  if (IDirectDrawSurface_SetClipper(lpDDS, lpDDC) != DD_OK)
287  return -1;
288  fb_hMemSet(&desc, 0, sizeof(DDSURFACEDESC));
289  desc.dwSize = sizeof(desc);
290  desc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
291  desc.dwWidth = fb_win32.w;
292  desc.dwHeight = fb_win32.h;
293  desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
294  if (IDirectDraw2_CreateSurface(lpDD, &desc, &lpDDS_back, 0) != DD_OK)
295  return -1;
296  } else {
297  lpDDS_back = lpDDS;
298  }
299 
300  format.dwSize = sizeof(format);
301  if (IDirectDrawSurface_GetPixelFormat(lpDDS, &format) != DD_OK)
302  return -1;
303  if (!(format.dwFlags & DDPF_RGB))
304  return -1;
305 
306  if (format.dwRGBBitCount == 8) {
307  if (IDirectDraw2_CreatePalette(lpDD, DDPCAPS_8BIT | DDPCAPS_INITIALIZE | DDPCAPS_ALLOW256,
308  fb_win32.palette, &lpDDP, NULL) != DD_OK)
309  return -1;
310  IDirectDrawSurface_SetPalette(lpDDS, lpDDP);
312  }
313 
314  depth = format.dwRGBBitCount;
315  if ((format.dwRGBBitCount == 16) && (format.dwGBitMask == 0x03E0))
316  depth = 15;
317  if ((format.dwRGBBitCount >= 24) && (format.dwRBitMask == 0xFF))
318  is_rgb = TRUE;
319  else if ((format.dwRGBBitCount >= 16) && (format.dwRBitMask == 0x1F))
320  is_rgb = TRUE;
321  fb_win32.blitter = fb_hGetBlitter(depth, is_rgb);
322  if (!fb_win32.blitter)
323  return -1;
324 
325  IDirectDraw2_GetMonitorFrequency(lpDD, (LPDWORD)&__fb_gfx->refresh_rate);
326 
327  vsync_event = CreateEvent(NULL, TRUE, FALSE, NULL);
328 
329  for (i = 0; i < 256; i++) {
330  __c_rgodfDIKeyboard[i].pguid = &__fb_GUID_Key;
331  __c_rgodfDIKeyboard[i].dwOfs = i;
332  __c_rgodfDIKeyboard[i].dwType = 0x8000000C | (i << 8);
333  __c_rgodfDIKeyboard[i].dwFlags = 0;
334  }
335  if (IDirectInput_CreateDevice(lpDI, &__fb_GUID_SysKeyboard, &lpDID, NULL) != DI_OK)
336  return -1;
337  if (IDirectInputDevice_SetDataFormat(lpDID, &__c_dfDIKeyboard) != DI_OK)
338  return -1;
339  if (IDirectInputDevice_Acquire(lpDID) != DI_OK)
340  return -1;
341 
342  return 0;
343 }
344 
345 static void directx_exit(void)
346 {
347  DDBLTFX bltfx;
348  RECT rect;
349 
350  if (!(fb_win32.flags & DRIVER_FULLSCREEN)) {
351  GetWindowRect(fb_win32.wnd, &rect);
352  win_x = rect.left;
353  win_y = rect.top;
354  }
355 
356  if (lpDI) {
357  if (lpDID) {
358  IDirectInputDevice_Unacquire(lpDID);
359  IDirectInputDevice_Release(lpDID);
360  lpDID = NULL;
361  }
362  IDirectInput_Release(lpDI);
363  lpDI = NULL;
364  }
365 
366  if (lpDD) {
367  if ((fb_win32.flags & DRIVER_FULLSCREEN) && lpDDS) {
368  bltfx.dwSize = sizeof(bltfx);
369  bltfx.dwDDFX = 0;
370  bltfx.dwFillColor = 0;
371  IDirectDrawSurface_Blt(lpDDS, &rect, NULL, NULL, DDBLT_COLORFILL, &bltfx);
372  }
373 
374  if (lpDDS) {
375  IDirectDrawSurface_Release(lpDDS);
376  lpDDS = NULL;
377  }
378  if ((!(fb_win32.flags & DRIVER_FULLSCREEN)) && (lpDDS_back)) {
379  IDirectDrawSurface_Release(lpDDS_back);
380  lpDDS_back = NULL;
381  }
382 
384  IDirectDraw2_RestoreDisplayMode(lpDD);
385  IDirectDraw2_SetCooperativeLevel(lpDD, fb_win32.wnd, DDSCL_NORMAL);
386  }
387  IDirectDraw2_Release(lpDD);
388  lpDD = NULL;
389  }
390  if (fb_win32.wnd) {
391  DestroyWindow(fb_win32.wnd);
392  fb_win32.wnd = NULL;
393  }
394  if (dd_library) {
395  FreeLibrary(dd_library);
396  dd_library = NULL;
397  }
398  if (di_library) {
399  FreeLibrary(di_library);
400  di_library = NULL;
401  }
402  if (vsync_event) {
403  CloseHandle(vsync_event);
404  vsync_event = NULL;
405  }
406 }
407 
408 #ifdef HOST_MINGW
409 static unsigned int WINAPI directx_thread( void *param )
410 #else
411 static DWORD WINAPI directx_thread( LPVOID param )
412 #endif
413 {
414  HANDLE running_event = param;
415  DDSURFACEDESC desc;
416  HRESULT result;
417  unsigned char keystate[256];
418  int i;
419 
420  if (directx_init()) return 1;
421 
422  SetEvent(running_event);
424 
425  while (fb_win32.is_running)
426  {
427  Sleep(10);
428 
429  fb_hWin32Lock();
430 
431  if (wait_vsync) {
432  WaitForSingleObject(vsync_event, 20);
433  wait_vsync = FALSE;
434  }
435 
438  IDirectDrawPalette_SetEntries(lpDDP, 0, 0, 256, fb_win32.palette);
440  }
441  desc.dwSize = sizeof(desc);
442  do {
444  result = IDirectDrawSurface_Lock(lpDDS_back, NULL, &desc, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL);
445  } while (result == DDERR_SURFACELOST);
446  if (result == DD_OK) {
447  fb_win32.blitter((unsigned char *)desc.lpSurface + display_offset * desc.lPitch, desc.lPitch);
448  IDirectDrawSurface_Unlock(lpDDS_back, desc.lpSurface);
450  }
451 
452  directx_paint();
453  }
454 
455  if( fb_win32.is_active ) {
456  result = IDirectInputDevice_GetDeviceState(lpDID, 256, keystate);
457  if ((result == DIERR_NOTACQUIRED) || (result == DIERR_INPUTLOST))
458  IDirectInputDevice_Acquire(lpDID);
459  else {
460  /* Simplicistic way to deal with extended scancodes */
461  for (i = 0; i < 128; i++)
462  __fb_gfx->key[i] = ((keystate[i] | keystate[i + 128]) & 0x80) ? TRUE : FALSE;
463  }
464  }
465 
467 
468  fb_hWin32Unlock();
469  }
470 
471  return 1;
472 }
473 
474 static int driver_init(char *title, int w, int h, int depth, int refresh_rate, int flags)
475 {
476  fb_hMemSet(&fb_win32, 0, sizeof(fb_win32));
477 
478  if (flags & (DRIVER_OPENGL | DRIVER_SHAPED_WINDOW))
479  return -1;
484 
485  win_x = (GetSystemMetrics(SM_CXSCREEN) - w) >> 1;
486  win_y = (GetSystemMetrics(SM_CYSCREEN) - h) >> 1;
487 
488  return fb_hWin32Init(title, w, h, MAX(8, depth), refresh_rate, flags);
489 }
490 
491 static void driver_wait_vsync(void)
492 {
493  ResetEvent(vsync_event);
494  fb_hWin32Lock();
495  wait_vsync = TRUE;
496  fb_hWin32Unlock();
497  IDirectDraw2_WaitForVerticalBlank(lpDD, DDWAITVB_BLOCKBEGIN, 0);
498  /* Signal vsync to updater thread */
499  SetEvent(vsync_event);
500 }
501 
502 static HRESULT CALLBACK fetch_modes_callback(LPDDSURFACEDESC desc, LPVOID data)
503 {
504  MODESLIST *modes = (MODESLIST *)data;
505  int depth = desc->ddpfPixelFormat.dwRGBBitCount;
506 
507  if ((depth == 16) && (desc->ddpfPixelFormat.dwGBitMask == 0x03E0))
508  depth = 15;
509  if (depth == modes->depth) {
510  modes->size++;
511  modes->data = (int *)realloc(modes->data, modes->size * sizeof(int));
512  modes->data[modes->size - 1] = (desc->dwWidth << 16) | desc->dwHeight;
513  }
514 
515  return DDENUMRET_OK;
516 }
517 
518 static int *driver_fetch_modes(int depth, int *size)
519 {
520  MODESLIST modes = { depth, 0, NULL };
521  LPDIRECTDRAW dd1;
522  LPDIRECTDRAW2 dd2;
523  DIRECTDRAWCREATE DirectDrawCreate;
524  HMODULE library = NULL;
525  HRESULT res;
526 
527  if (!lpDD) {
528  library = (HMODULE)LoadLibrary("ddraw.dll");
529  if (!library)
530  return NULL;
531  DirectDrawCreate = (DIRECTDRAWCREATE)GetProcAddress(library, "DirectDrawCreate");
532  if ((!DirectDrawCreate) || (DirectDrawCreate(NULL, &dd1, NULL) != DD_OK)) {
533  FreeLibrary(library);
534  return NULL;
535  }
536  res = IDirectDraw_QueryInterface(dd1, &__fb_IID_IDirectDraw2, (LPVOID)&dd2);
537  IDirectDraw_Release(dd1);
538  if (res != DD_OK) {
539  FreeLibrary(library);
540  return NULL;
541  }
542  } else {
543  dd2 = lpDD;
544  }
545  if (IDirectDraw2_EnumDisplayModes(dd2, DDEDM_STANDARDVGAMODES, NULL, (LPVOID)&modes, fetch_modes_callback) != DD_OK)
546  modes.data = NULL;
547 
548  if (!lpDD) {
549  IDirectDraw_Release(dd2);
550  FreeLibrary(library);
551  }
552 
553  *size = modes.size;
554  return modes.data;
555 }
556 
557 #endif