1 /*
2  * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include "sun_java2d_d3d_D3DGraphicsDevice.h"
27 #include "D3DGraphicsDevice.h"
28 #include "D3DPipelineManager.h"
29 #include "D3DRenderQueue.h"
30 #include "Trace.h"
31 #include "awt_Toolkit.h"
32 #include "awt_Window.h"
33 
34 extern jobject CreateDisplayMode(JNIEnv* env, jint width, jint height,
35                                  jint bitDepth, jint refreshRate);
36 extern void addDisplayMode(JNIEnv* env, jobject arrayList, jint width,
37                            jint height, jint bitDepth, jint refreshRate);
38 
39 extern "C" {
40 /*
41  * Class:     sun_java2d_d3d_D3DGraphicsDevice
42  * Method:    initD3D
43  * Signature: ()Z
44  */
Java_sun_java2d_d3d_D3DGraphicsDevice_initD3D(JNIEnv * env,jclass)45 JNIEXPORT jboolean JNICALL Java_sun_java2d_d3d_D3DGraphicsDevice_initD3D
46   (JNIEnv *env, jclass)
47 {
48     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_initD3D");
49 
50     jboolean result = D3DInitializer::GetInstance().EnsureInited()
51                       ? JNI_TRUE : JNI_FALSE;
52     J2dTraceLn1(J2D_TRACE_INFO, "D3DGD_initD3D: result=%x", result);
53     return result;
54 }
55 
56 /*
57  * Class:     sun_java2d_d3d_D3DGraphicsDevice
58  * Method:    getDeviceIdNative
59  * Signature: (I)Ljava/lang/String;
60  */
Java_sun_java2d_d3d_D3DGraphicsDevice_getDeviceIdNative(JNIEnv * env,jclass d3dsdc,jint gdiScreen)61 JNIEXPORT jstring JNICALL Java_sun_java2d_d3d_D3DGraphicsDevice_getDeviceIdNative
62   (JNIEnv *env, jclass d3dsdc, jint gdiScreen)
63 {
64     D3DPipelineManager *pMgr;
65     UINT adapter;
66     D3DADAPTER_IDENTIFIER9 aid;
67     IDirect3D9 *pd3d9;
68 
69     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_getDeviceIdNative");
70 
71     pMgr = D3DPipelineManager::GetInstance();
72     RETURN_STATUS_IF_NULL(pMgr, NULL);
73     pd3d9 = pMgr->GetD3DObject();
74     RETURN_STATUS_IF_NULL(pd3d9, NULL);
75 
76     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
77     if (FAILED(pd3d9->GetAdapterIdentifier(adapter, 0, &aid))) {
78         return NULL;
79     }
80 
81     // ('%d.' will take no more than 6+1 chars since we are printing a WORD)
82     //            AAAA&BBBB MAX_DEVICE_IDENTIFIER_STRING (%d.%d.%d.%d)0
83     size_t len = (4+1+4  +1+MAX_DEVICE_IDENTIFIER_STRING+1 +1+(6+1)*4+1 +1);
84     WCHAR *pAdapterId = new WCHAR[len];
85     RETURN_STATUS_IF_NULL(pAdapterId, NULL);
86 
87     _snwprintf(pAdapterId, len, L"%x&%x %S (%d.%d.%d.%d)",
88                0xffff & aid.VendorId, 0xffff & aid.DeviceId, aid.Description,
89                HIWORD(aid.DriverVersion.HighPart),
90                LOWORD(aid.DriverVersion.HighPart),
91                HIWORD(aid.DriverVersion.LowPart),
92                LOWORD(aid.DriverVersion.LowPart));
93     // _snwprintf doesn't add 0 at the end if the formatted string didn't fit
94     // in the buffer so we have to make sure it is null terminated
95     pAdapterId[len-1] = (WCHAR)0;
96 
97     J2dTraceLn1(J2D_TRACE_VERBOSE, "  id=%S", pAdapterId);
98 
99     jstring ret = JNU_NewStringPlatform(env, pAdapterId);
100 
101     delete[] pAdapterId;
102 
103     return ret;
104 }
105 
106 /*
107  * Class:     sun_java2d_d3d_D3DGraphicsDevice
108  * Method:    getDeviceCapsNative
109  * Signature: (I)I
110  */
111 JNIEXPORT jint JNICALL
Java_sun_java2d_d3d_D3DGraphicsDevice_getDeviceCapsNative(JNIEnv * env,jclass d3dsdc,jint gdiScreen)112 Java_sun_java2d_d3d_D3DGraphicsDevice_getDeviceCapsNative
113   (JNIEnv *env, jclass d3dsdc, jint gdiScreen)
114 {
115     D3DPipelineManager *pMgr;
116     D3DContext *pCtx;
117     UINT adapter;
118 
119     J2dRlsTraceLn(J2D_TRACE_INFO, "D3DGD_getDeviceCapsNative");
120 
121     pMgr = D3DPipelineManager::GetInstance();
122     RETURN_STATUS_IF_NULL(pMgr, CAPS_EMPTY);
123     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
124 
125     if (FAILED(pMgr->GetD3DContext(adapter, &pCtx))) {
126         J2dRlsTraceLn1(J2D_TRACE_ERROR,
127                       "D3DGD_getDeviceCapsNative: device %d disabled", adapter);
128         return CAPS_EMPTY;
129     }
130     return pCtx->GetContextCaps();
131 }
132 
133 /*
134  * Class:     sun_java2d_d3d_D3DGraphicsDevice
135  * Method:    enterFullScreenExclusiveNative
136  * Signature: (IJ)V
137  */
138 JNIEXPORT jboolean JNICALL
Java_sun_java2d_d3d_D3DGraphicsDevice_enterFullScreenExclusiveNative(JNIEnv * env,jclass gdc,jint gdiScreen,jlong window)139 Java_sun_java2d_d3d_D3DGraphicsDevice_enterFullScreenExclusiveNative
140   (JNIEnv *env, jclass gdc, jint gdiScreen, jlong window)
141 {
142     HRESULT res;
143     D3DPipelineManager *pMgr;
144     D3DContext *pCtx;
145     HWND hWnd;
146     AwtWindow *w;
147     D3DPRESENT_PARAMETERS newParams, *pCurParams;
148     D3DDISPLAYMODE dm;
149     UINT adapter;
150 
151     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_enterFullScreenExclusiveNative");
152 
153     RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);
154     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
155 
156     if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
157         D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
158         return JNI_FALSE;
159     }
160 
161     w = (AwtWindow *)AwtComponent::GetComponent((HWND)window);
162     if (w == NULL || !::IsWindow(hWnd = w->GetTopLevelHWnd())) {
163         J2dTraceLn(J2D_TRACE_WARNING,
164                    "D3DGD_enterFullScreenExclusiveNative: disposed window");
165         return JNI_FALSE;
166     }
167 
168     // REMIND: should we also move the non-topleve window from
169     // being on top here (it's moved to front in GraphicsDevice.setFSW())?
170 
171     pCtx->Get3DObject()->GetAdapterDisplayMode(adapter, &dm);
172     pCurParams = pCtx->GetPresentationParams();
173 
174     // let the mananger know that we're entering the fs mode, it will
175     // set the proper current focus window for us, which ConfigureContext will
176     // use when creating the device
177     pMgr->SetFSFocusWindow(adapter, hWnd);
178 
179     newParams = *pCurParams;
180     newParams.hDeviceWindow = hWnd;
181     newParams.Windowed = FALSE;
182     newParams.BackBufferCount = 1;
183     newParams.BackBufferFormat = dm.Format;
184     newParams.FullScreen_RefreshRateInHz = dm.RefreshRate;
185     newParams.BackBufferWidth = dm.Width;
186     newParams.BackBufferHeight = dm.Height;
187     newParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
188     newParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
189 
190     res = pCtx->ConfigureContext(&newParams);
191     D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
192     return SUCCEEDED(res);
193 }
194 
195 /*
196  * Class:     sun_java2d_d3d_D3DGraphicsDevice
197  * Method:    exitFullScreenExclusiveNative
198  * Signature: (I)V
199  */
200 JNIEXPORT jboolean JNICALL
Java_sun_java2d_d3d_D3DGraphicsDevice_exitFullScreenExclusiveNative(JNIEnv * env,jclass gdc,jint gdiScreen)201 Java_sun_java2d_d3d_D3DGraphicsDevice_exitFullScreenExclusiveNative
202   (JNIEnv *env, jclass gdc, jint gdiScreen)
203 {
204     HRESULT res;
205     D3DPipelineManager *pMgr;
206     D3DContext *pCtx;
207     D3DPRESENT_PARAMETERS newParams, *pCurParams;
208     UINT adapter;
209 
210     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_exitFullScreenExclusiveNative");
211 
212     RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);
213     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
214 
215     if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
216         D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
217         return JNI_FALSE;
218     }
219 
220     pCurParams = pCtx->GetPresentationParams();
221 
222     newParams = *pCurParams;
223     // we're exiting fs, the device window can be 0
224     newParams.hDeviceWindow = 0;
225     newParams.Windowed = TRUE;
226     newParams.BackBufferFormat = D3DFMT_UNKNOWN;
227     newParams.BackBufferCount = 1;
228     newParams.FullScreen_RefreshRateInHz = 0;
229     newParams.BackBufferWidth = 0;
230     newParams.BackBufferHeight = 0;
231     newParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
232     newParams.SwapEffect = D3DSWAPEFFECT_COPY;
233 
234     res = pCtx->ConfigureContext(&newParams);
235     D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
236 
237     // exited fs, update current focus window
238     // note that we call this after this adapter exited fs mode so that
239     // the rest of the adapters can be reset
240     pMgr->SetFSFocusWindow(adapter, 0);
241 
242     return SUCCEEDED(res);
243 }
244 
245 /*
246  * Class:     sun_java2d_d3d_D3DGraphicsDevice
247  * Method:    configDisplayModeNative
248  * Signature: (IJIIII)V
249  */
250 JNIEXPORT void JNICALL
Java_sun_java2d_d3d_D3DGraphicsDevice_configDisplayModeNative(JNIEnv * env,jclass gdc,jint gdiScreen,jlong window,jint width,jint height,jint bitDepth,jint refreshRate)251 Java_sun_java2d_d3d_D3DGraphicsDevice_configDisplayModeNative
252   (JNIEnv *env, jclass gdc, jint gdiScreen, jlong window,
253    jint width, jint height, jint bitDepth, jint refreshRate)
254 {
255     HRESULT res;
256     D3DPipelineManager *pMgr;
257     D3DContext *pCtx;
258     D3DPRESENT_PARAMETERS newParams, *pCurParams;
259     UINT adapter;
260 
261     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_configDisplayModeNative");
262 
263     RETURN_IF_NULL(pMgr = D3DPipelineManager::GetInstance());
264     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
265 
266     if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
267         D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
268         return;
269     }
270 
271     pCurParams = pCtx->GetPresentationParams();
272 
273     newParams = *pCurParams;
274     newParams.BackBufferWidth = width;
275     newParams.BackBufferHeight = height;
276     newParams.FullScreen_RefreshRateInHz = refreshRate;
277     newParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
278     // we leave the swap effect so that it's more likely
279     // to be the one user selected initially
280 //    newParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
281 
282     if (bitDepth == 32) {
283         newParams.BackBufferFormat = D3DFMT_X8R8G8B8;
284     } else if (bitDepth == 16) {
285         UINT modeNum;
286         D3DDISPLAYMODE mode;
287         IDirect3D9 *pd3d9;
288         UINT modesCount;
289 
290         RETURN_IF_NULL(pd3d9 = pMgr->GetD3DObject());
291 
292         modesCount = pd3d9->GetAdapterModeCount(adapter, D3DFMT_R5G6B5);
293         if (modesCount == 0) {
294             modesCount = pd3d9->GetAdapterModeCount(adapter, D3DFMT_X1R5G5B5);
295         }
296 
297         newParams.BackBufferFormat = D3DFMT_UNKNOWN;
298         for (modeNum = 0; modeNum < modesCount; modeNum++) {
299             if (SUCCEEDED(pd3d9->EnumAdapterModes(adapter, D3DFMT_R5G6B5,
300                                                   modeNum, &mode)))
301             {
302                 if (mode.Width == width && mode.Height == height &&
303                     mode.RefreshRate == refreshRate)
304                 {
305                     // prefer 565 over 555
306                     if (mode.Format == D3DFMT_R5G6B5) {
307                         newParams.BackBufferFormat = D3DFMT_R5G6B5;
308                         break;
309                     } else if (mode.Format == D3DFMT_X1R5G5B5) {
310                         newParams.BackBufferFormat = D3DFMT_X1R5G5B5;
311                     }
312                 }
313             }
314         }
315         if (newParams.BackBufferFormat == D3DFMT_UNKNOWN) {
316             J2dRlsTraceLn(J2D_TRACE_ERROR,
317                           "D3DGD_configDisplayModeNative: no 16-bit formats");
318             return;
319         }
320     } else {
321         J2dRlsTraceLn1(J2D_TRACE_ERROR,
322                        "D3DGD_configDisplayModeNative: unsupported depth: %d",
323                        bitDepth);
324         return;
325     }
326 
327     J2dTraceLn4(J2D_TRACE_VERBOSE, "  changing to dm: %dx%dx%d@%d",
328                 newParams.BackBufferWidth, newParams.BackBufferHeight,
329                 bitDepth, refreshRate);
330     J2dTraceLn1(J2D_TRACE_VERBOSE, "  selected backbuffer format: %d",
331                 newParams.BackBufferFormat);
332 
333     res = pCtx->ConfigureContext(&newParams);
334     if (SUCCEEDED(res)) {
335         // the full screen window doesn't receive WM_SIZE event when
336         // the display mode changes (it does get resized though) so we need to
337         // generate the event ourselves
338         ::SendMessage(newParams.hDeviceWindow, WM_SIZE, width, height);
339     }
340     D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
341 }
342 
343 
344 /*
345  * Class:     sun_java2d_d3d_D3DGraphicsDevice
346  * Method:    getCurrentDisplayModeNative
347  * Signature: (I)Ljava/awt/DisplayMode;
348  */
349 JNIEXPORT jobject JNICALL
Java_sun_java2d_d3d_D3DGraphicsDevice_getCurrentDisplayModeNative(JNIEnv * env,jclass gdc,jint gdiScreen)350 Java_sun_java2d_d3d_D3DGraphicsDevice_getCurrentDisplayModeNative
351   (JNIEnv *env, jclass gdc, jint gdiScreen)
352 {
353     D3DPipelineManager *pMgr;
354     IDirect3D9 *pd3d9;
355     jobject ret = NULL;
356     D3DDISPLAYMODE mode;
357     UINT adapter;
358 
359     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_getCurrentDisplayModeNative");
360 
361     RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), NULL);
362     RETURN_STATUS_IF_NULL(pd3d9 = pMgr->GetD3DObject(), NULL);
363     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
364 
365     if (SUCCEEDED(pd3d9->GetAdapterDisplayMode(adapter, &mode))) {
366         int bitDepth = -1;
367         // these are the only three valid screen formats
368         switch (mode.Format) {
369             case D3DFMT_X8R8G8B8: bitDepth = 32; break;
370             case D3DFMT_R5G6B5:
371             case D3DFMT_X1R5G5B5: bitDepth = 16; break;
372         }
373         J2dTraceLn4(J2D_TRACE_VERBOSE,
374                     "  current dm: %dx%dx%d@%d",
375                     mode.Width, mode.Height, bitDepth, mode.RefreshRate);
376         ret = CreateDisplayMode(env, mode.Width, mode.Height, bitDepth,
377                                 mode.RefreshRate);
378     }
379     return ret;
380 }
381 
382 /*
383  * Class:     sun_java2d_d3d_D3DGraphicsDevice
384  * Method:    enumDisplayModesNative
385  * Signature: (ILjava/util/ArrayList;)V
386  */
387 JNIEXPORT void JNICALL
Java_sun_java2d_d3d_D3DGraphicsDevice_enumDisplayModesNative(JNIEnv * env,jclass gdc,jint gdiScreen,jobject arrayList)388 Java_sun_java2d_d3d_D3DGraphicsDevice_enumDisplayModesNative
389   (JNIEnv *env, jclass gdc, jint gdiScreen, jobject arrayList)
390 {
391     D3DPipelineManager *pMgr;
392     IDirect3D9 *pd3d9;
393     jobject ret = NULL;
394     D3DDISPLAYMODE mode;
395     UINT formatNum, modeNum, modesCount;
396     UINT adapter;
397     // EnumAdapterModes treats 555 and 565 formats as equivalents
398     static D3DFORMAT formats[] =
399       { D3DFMT_X8R8G8B8, D3DFMT_R5G6B5 };
400 
401     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_enumDisplayModesNative");
402 
403     RETURN_IF_NULL(pMgr = D3DPipelineManager::GetInstance());
404     RETURN_IF_NULL(pd3d9 = pMgr->GetD3DObject());
405     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
406 
407     for (formatNum = 0; formatNum < (sizeof formats)/(sizeof *formats); formatNum++) {
408         modesCount = pd3d9->GetAdapterModeCount(adapter, formats[formatNum]);
409         for (modeNum = 0; modeNum < modesCount; modeNum++) {
410             if (SUCCEEDED(pd3d9->EnumAdapterModes(adapter, formats[formatNum],
411                                                   modeNum, &mode)))
412             {
413                 int bitDepth = -1;
414                 // these are the only three valid screen formats,
415                 // 30-bit is returned as X8R8G8B8
416                 switch (mode.Format) {
417                     case D3DFMT_X8R8G8B8: bitDepth = 32; break;
418                     case D3DFMT_R5G6B5:
419                     case D3DFMT_X1R5G5B5: bitDepth = 16; break;
420                 }
421                 J2dTraceLn4(J2D_TRACE_VERBOSE, "  found dm: %dx%dx%d@%d",
422                             mode.Width, mode.Height, bitDepth,mode.RefreshRate);
423                 addDisplayMode(env, arrayList, mode.Width, mode.Height,
424                                bitDepth, mode.RefreshRate);
425             }
426         }
427     }
428 }
429 
430 /*
431  * Class:     sun_java2d_d3d_D3DGraphicsDevice
432  * Method:    getAvailableAcceleratedMemoryNative
433  * Signature: (I)J
434  */
435 JNIEXPORT jlong JNICALL
Java_sun_java2d_d3d_D3DGraphicsDevice_getAvailableAcceleratedMemoryNative(JNIEnv * env,jclass gdc,jint gdiScreen)436 Java_sun_java2d_d3d_D3DGraphicsDevice_getAvailableAcceleratedMemoryNative
437   (JNIEnv *env, jclass gdc, jint gdiScreen)
438 {
439     // REMIND: looks like Direct3D provides information about texture memory
440     // only via IDirect3DDevice9::GetAvailableTextureMem, however, it
441     // seems to report the same amount as direct draw used to.
442     HRESULT res;
443     D3DPipelineManager *pMgr;
444     D3DContext *pCtx;
445     IDirect3DDevice9 *pd3dDevice;
446     UINT adapter;
447 
448     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_getAvailableAcceleratedMemoryNative");
449 
450     RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), 0L);
451     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
452 
453     if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
454         D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
455         return 0L;
456     }
457     RETURN_STATUS_IF_NULL(pd3dDevice = pCtx->Get3DDevice(), 0L);
458 
459     UINT mem = pd3dDevice->GetAvailableTextureMem();
460     J2dTraceLn1(J2D_TRACE_VERBOSE, "  available memory=%d", mem);
461     return mem;
462 }
463 
464 /*
465  * Class:     sun_java2d_d3d_D3DGraphicsDevice
466  * Method:    isD3DAvailableOnDeviceNative
467  * Signature: (I)Z
468  */
469 JNIEXPORT jboolean JNICALL
Java_sun_java2d_d3d_D3DGraphicsDevice_isD3DAvailableOnDeviceNative(JNIEnv * env,jclass gdc,jint gdiScreen)470 Java_sun_java2d_d3d_D3DGraphicsDevice_isD3DAvailableOnDeviceNative
471   (JNIEnv *env, jclass gdc, jint gdiScreen)
472 {
473     HRESULT res;
474     D3DPipelineManager *pMgr;
475     D3DContext *pCtx;
476     UINT adapter;
477 
478     J2dTraceLn(J2D_TRACE_INFO, "D3DGD_isD3DAvailableOnDeviceNative");
479 
480     RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);
481     adapter = pMgr->GetAdapterOrdinalForScreen(gdiScreen);
482 
483     if (FAILED(res = pMgr->GetD3DContext(adapter, &pCtx))) {
484         D3DRQ_MarkLostIfNeeded(res, D3DRQ_GetCurrentDestination());
485         return JNI_FALSE;
486     }
487 
488     return JNI_TRUE;
489 }
490 
491 } // extern "C"
492