/////////////////////////////////////////////////////// /// /// このソースコードを引用・改変した結果如何なる /// 損害が発生しても、著者はは責任を負いません。 /// 広島大学 脳外科 橋詰顕 /// //////////////////////////////////////////////////////// #define STRICT //型チェックを厳密に行う #define WIN32_LEAN_AND_MEAN //ヘッダーからあまり使われていない関数を省く #include #include //三角関数用 //timeGetTime用 #include //timeGetTime 本来windows.hに含まれているはずだが明示しないとだめみたい #pragma comment(lib,"winmm.lib") //wav file用でもある //GIVEIO.sys用 #include //_outpに必要 BOOL CanGIVEIO=TRUE; //GIVEIO.sysを使ってパラレルポートの制御をするかどうか //DirectDraw7用 #include #pragma comment(lib,"ddraw.lib") #pragma comment(lib,"dxguid.lib") void hnsDeleteWindowMessage(); void hnsDrawCheckerBoardOnOffScreenSurface(LPDIRECTDRAWSURFACE7 pSurface,int WindowWidth,int WindowHeight,int fragment);HANDLE hnsEnableGIVEIO(); LPDIRECTDRAWSURFACE7 hnsMakeDirectDraw7BackSurface(LPDIRECTDRAWSURFACE7 pSurface); LPDIRECTDRAW7 hnsMakeDirectDraw7Object(HWND hWindow,int WindowWidth,int WindowHeight); LPDIRECTDRAWSURFACE7 hnsMakeDirectDraw7OffScreenSurface(LPDIRECTDRAW7 pDirectDraw,DWORD WindowWidth,DWORD WindowHeight); LPDIRECTDRAWSURFACE7 hnsMakeDirectDraw7PrimarySurface(LPDIRECTDRAW7 pDirectDraw,int BackBuffer); void hnsSetFullScreenVGA(HWND hWindow); void hnsTaskLoop(HWND hWindow); void hnsTriggerGIVEIO(int num); LRESULT CALLBACK WindowProcedure(HWND hWindow,UINT message,WPARAM wParameter,LPARAM lParameter); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow) { //WNDCLASSの設計 WNDCLASS WindowClass; WindowClass.style=CS_HREDRAW | CS_VREDRAW; WindowClass.lpfnWndProc=WindowProcedure; WindowClass.cbClsExtra=0; WindowClass.cbWndExtra=0; WindowClass.hInstance=hInstance; WindowClass.hIcon=LoadIcon(NULL,IDI_APPLICATION); WindowClass.hCursor=LoadCursor(NULL,IDC_ARROW); WindowClass.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);//背景は黒 WindowClass.lpszMenuName=NULL; WindowClass.lpszClassName="WindowClassName"; //設計したWNDCLASSの登録 if(!RegisterClass(&WindowClass)) { MessageBox(NULL,"設計したWNDCLASSの設計に失敗しました","Error!",MB_ICONERROR); return 0; } //Windowの作成 HWND hWindow=CreateWindow( "WindowClassName", //window class name "Let's make MEG task", //title //WS_OVERLAPPEDWINDOW | WS_VISIBLE, //Window Style WS_POPUP | WS_VISIBLE, //枠なし表示 CW_USEDEFAULT,CW_USEDEFAULT, //top left CW_USEDEFAULT,CW_USEDEFAULT, //width,height NULL, //parent window handle (HMENU)NULL, //window menu handle hInstance, //application handle NULL); //creation parameter ShowWindow(hWindow,SW_SHOW); //GIVEIO.sysを有効にする HANDLE h=hnsEnableGIVEIO(); MSG message; //無限ループです。 while(GetMessage(&message,NULL,0,0)) { TranslateMessage(&message); DispatchMessage(&message); } //GIVEIOを終了する。 if(h!=NULL){CloseHandle(h);} return 0; } void hnsDeleteWindowMessage() { //Window Messageを取り除く MSG message; int flag=1; while (1) { flag=::PeekMessage(&message,NULL,WM_KEYFIRST,WM_KEYLAST,PM_REMOVE ); if(flag==0){break;} } } void hnsDrawCheckerBoardOnOffScreenSurface(LPDIRECTDRAWSURFACE7 pSurface,int WindowWidth,int WindowHeight,int fragment) { HDC hSurfaceDeviceContext; pSurface->GetDC(&hSurfaceDeviceContext); //ペンとブラシの作成 COLORREF color; color=RGB(255,255,255);//白 HBRUSH hWhiteBrush=CreateSolidBrush(color); HPEN hWhitePen =CreatePen(PS_SOLID,1,color); color=RGB(0,0,0);//黒 HBRUSH hBlackBrush=CreateSolidBrush(color); HPEN hBlackPen =CreatePen(PS_SOLID,1,color); color=RGB(255,0,0);//赤 HBRUSH hRedBrush=CreateSolidBrush(color); HPEN hRedPen =CreatePen(PS_SOLID,1,color); int x,y,xx,yy; int CellWidth=(int)(WindowWidth/fragment); int CellHeight=(int)(WindowHeight/fragment); bool check=TRUE; //市松模様の描画 for(y=0;yReleaseDC(hSurfaceDeviceContext); return; } HANDLE hnsEnableGIVEIO() { if(!CanGIVEIO){return NULL;}//GIVEIOを最初から使わないとき //Windows2000/XPの時のGIVEIO.sysの設定 HANDLE h=CreateFile( (LPCTSTR)"\\\\.\\giveio", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(h==INVALID_HANDLE_VALUE) { MessageBox(NULL,TEXT("GIVEIOは使えません"),TEXT("GIVEIO.sys check"),MB_ICONERROR); CanGIVEIO=TRUE; } return h; } LPDIRECTDRAWSURFACE7 hnsMakeDirectDraw7BackSurface(LPDIRECTDRAWSURFACE7 pSurface) { //pSurfaceはprimary surface,back surface,back surface,...の順で取得すること DDSCAPS2 DirectDrawSurfaceCaps; LPDIRECTDRAWSURFACE7 pBackSurface; ZeroMemory(&DirectDrawSurfaceCaps,sizeof(DirectDrawSurfaceCaps)); DirectDrawSurfaceCaps.dwCaps=DDSCAPS_BACKBUFFER; HRESULT hResult=pSurface->GetAttachedSurface(&DirectDrawSurfaceCaps,&pBackSurface); if(FAILED(hResult)){return NULL;} return pBackSurface; } LPDIRECTDRAW7 hnsMakeDirectDraw7Object(HWND hWindow,int WindowWidth,int WindowHeight) { HRESULT hResult; LPDIRECTDRAW7 pDirectDraw=NULL; hResult=DirectDrawCreateEx(NULL,(void**)&pDirectDraw,IID_IDirectDraw7,NULL); if(FAILED(hResult)){ MessageBox(NULL,"DirectDraw objectは作成できませんでした","",MB_ICONERROR); return NULL; } pDirectDraw->SetCooperativeLevel( hWindow, DDSCL_EXCLUSIVE | //Device占有 DDSCL_ALLOWREBOOT | //CTRL + ALT + DEL の機能を許可する DDSCL_FULLSCREEN); //fullscreen hResult=pDirectDraw->SetDisplayMode( WindowWidth,WindowHeight,//dwWidth,dwHeight 32, //dwBPP Bit/Pixel checkすること! 0, //dwRefeshRate 0:default refresh rate 0); //dwFlags if(FAILED(hResult)){ MessageBox(NULL,"Display Modeの設定はできませんでした","",MB_ICONERROR); return NULL; } pDirectDraw->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,0);//垂直描画開始にあわせる return pDirectDraw; } LPDIRECTDRAWSURFACE7 hnsMakeDirectDraw7OffScreenSurface(LPDIRECTDRAW7 pDirectDraw,DWORD WindowWidth,DWORD WindowHeight) { //off screen surfaceをdirect draw objectから作成 DDSURFACEDESC2 DirectDrawSurfaceDesc; LPDIRECTDRAWSURFACE7 pSurface; ZeroMemory(&DirectDrawSurfaceDesc,sizeof(DirectDrawSurfaceDesc)); DirectDrawSurfaceDesc.dwSize=sizeof(DirectDrawSurfaceDesc); DirectDrawSurfaceDesc.dwFlags=DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; DirectDrawSurfaceDesc.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN; DirectDrawSurfaceDesc.dwWidth=WindowWidth; DirectDrawSurfaceDesc.dwHeight=WindowHeight; pDirectDraw->CreateSurface(&DirectDrawSurfaceDesc,&pSurface,NULL); return pSurface; } LPDIRECTDRAWSURFACE7 hnsMakeDirectDraw7PrimarySurface(LPDIRECTDRAW7 pDirectDraw,int BackBuffer) { LPDIRECTDRAWSURFACE7 pPrimarySurface; DDSURFACEDESC2 DirectDrawSurfaceDesc; ZeroMemory(&DirectDrawSurfaceDesc,sizeof(DirectDrawSurfaceDesc)); DirectDrawSurfaceDesc.dwSize=sizeof(DirectDrawSurfaceDesc); if(BackBuffer==0) { DirectDrawSurfaceDesc.dwFlags=DDSD_CAPS; DirectDrawSurfaceDesc.dwRefreshRate=0;//default? DirectDrawSurfaceDesc.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE; } else { DirectDrawSurfaceDesc.dwFlags= DDSD_CAPS | //ddsCapsを有効に DDSD_BACKBUFFERCOUNT;//back bufferを有効に DirectDrawSurfaceDesc.ddsCaps.dwCaps= DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; DirectDrawSurfaceDesc.dwBackBufferCount=BackBuffer; } HRESULT hResult=pDirectDraw->CreateSurface(&DirectDrawSurfaceDesc,&pPrimarySurface,NULL); if(FAILED(hResult)){return NULL;} return pPrimarySurface; } void hnsSetFullScreenVGA(HWND hWindow) { static int WindowWidth=640; static int WindowHeight=480; LONG lResult; //画面を640x480に変更できたかどうか //画面を640x480にする DEVMODE DeviceMode; DeviceMode.dmSize =sizeof(DEVMODE); DeviceMode.dmFields =DM_PELSWIDTH | DM_PELSHEIGHT; DeviceMode.dmPelsWidth =WindowWidth; DeviceMode.dmPelsHeight =WindowHeight; //画面を元に戻す if(hWindow==NULL){ ChangeDisplaySettings(&DeviceMode,NULL); return; } lResult=ChangeDisplaySettings(&DeviceMode,CDS_FULLSCREEN); if(lResult!=DISP_CHANGE_SUCCESSFUL) { MessageBox(hWindow,"full screenの変換は失敗しました","",MB_ICONERROR); return; } SetWindowPos(hWindow,HWND_TOPMOST,0,0,WindowWidth,WindowHeight,0); } void hnsTaskLoop(HWND hWindow) { static int WindowWidth=640; static int WindowHeight=480; //DirectDraw objectの作成 LPDIRECTDRAW7 pDirectDraw=hnsMakeDirectDraw7Object(hWindow,WindowWidth,WindowHeight); if(pDirectDraw==NULL) { return;//失敗したらやめる } //DirectDraw objectからprimary surfaceの作成 LPDIRECTDRAWSURFACE7 pPrimarySurface=hnsMakeDirectDraw7PrimarySurface(pDirectDraw,1); if(pPrimarySurface==NULL) { pDirectDraw->Release(); return; } //primary surfaceからback surfaceの作成 LPDIRECTDRAWSURFACE7 pBackSurface=hnsMakeDirectDraw7BackSurface(pPrimarySurface); if(pPrimarySurface==NULL) { pPrimarySurface->Release(); pDirectDraw->Release(); return; } //off screen surfaceの作成 int x; LPDIRECTDRAWSURFACE7 pOffScreenSurface[5]; for(x=0;x<5;++x) { pOffScreenSurface[x]=hnsMakeDirectDraw7OffScreenSurface(pDirectDraw,WindowWidth,WindowHeight); } if(pOffScreenSurface[4]==NULL) { pBackSurface->Release(); pPrimarySurface->Release(); pDirectDraw->Release(); return; } //off screen surfaceに市松模様を描画 int fragment=20; int CellWidth=WindowWidth/fragment; int CellHeight=WindowHeight/fragment; hnsDrawCheckerBoardOnOffScreenSurface(pOffScreenSurface[0],WindowWidth,WindowHeight,fragment); //反転市松模様の作成 RECT rectAll,rectLeftUpper,rectLeftLower,rectRightUpper,rectRightLower; SetRect(&rectAll,0,0,WindowWidth,WindowHeight); SetRect(&rectLeftUpper,0,0,CellWidth*(fragment/2-1),CellHeight*(fragment/2-1)); SetRect(&rectLeftLower,0,CellHeight*(fragment/2+1),rectLeftUpper.right,WindowHeight); SetRect(&rectRightUpper,CellWidth*(fragment/2+1),0,WindowWidth,rectLeftUpper.bottom); SetRect(&rectRightLower,rectRightUpper.left,rectLeftLower.top,WindowWidth,WindowHeight); for(x=1;x<5;++x) { pOffScreenSurface[x]->Blt(&rectAll,pOffScreenSurface[0],&rectAll,DDBLT_WAIT,NULL); } pOffScreenSurface[1]->Blt(&rectLeftUpper,pOffScreenSurface[0],&rectLeftLower,DDBLT_WAIT,NULL); pOffScreenSurface[2]->Blt(&rectLeftLower,pOffScreenSurface[0],&rectLeftUpper,DDBLT_WAIT,NULL); pOffScreenSurface[3]->Blt(&rectRightUpper,pOffScreenSurface[0],&rectRightLower,DDBLT_WAIT,NULL); pOffScreenSurface[4]->Blt(&rectRightLower,pOffScreenSurface[0],&rectRightUpper,DDBLT_WAIT,NULL); BYTE TriggerFlag=0; BYTE trigger[]={0,1,2,4,8}; int WaitTime=500;//刺激間隔 srand(timeGetTime()); do //御法度の無限ループ { pBackSurface->BltFast(0,0,pOffScreenSurface[0],&rectAll,DDBLTFAST_WAIT ); pPrimarySurface->Flip(NULL,DDFLIP_WAIT); //パラレルポートにトリガー信号を出力 hnsTriggerGIVEIO(trigger[TriggerFlag]); //どの画面にするか選択 TriggerFlag=(rand()%256)%4+1; //休憩 Sleep(WaitTime-50+rand()%100); hnsTriggerGIVEIO(0); //表示 pBackSurface->BltFast(0,0,pOffScreenSurface[TriggerFlag],&rectAll,DDBLTFAST_WAIT ); //pPrimarySurface->Flip(NULL,DDFLIP_WAIT); pPrimarySurface->Flip(NULL,DDFLIP_NOVSYNC);//次の走査線にできる限り近い物理的なフリップを実行する //パラレルポートにトリガー信号を出力 hnsTriggerGIVEIO(trigger[TriggerFlag]); //休憩 Sleep(WaitTime-50+rand()%100); hnsTriggerGIVEIO(0); }while((GetAsyncKeyState(VK_ESCAPE)&0x8000)==0); //Direct Drawで作った部品を作成順の逆で解放 for(x=0;x<5;++x) { pOffScreenSurface[x]->Release(); } pBackSurface->Release(); pPrimarySurface->Release(); pDirectDraw->Release(); //Window Messageを取り除く hnsDeleteWindowMessage(); } void hnsTriggerGIVEIO(int num) { //パラレルポートの設定 const unsigned short data_port =0x00378;//D7,D6,D5,D4,D3,D2,D1,D0 const unsigned short status_port =0x00379;//^BSY,^ACK,PE,SLCT,^ERR,・・・,・・・,・・・ //^BSY=0 printer is busy //^ACK=0 ACK受信 //PE=1 ペーパーエンド //^ERR=0 printer error const unsigned short control_port=0x0037A;//・・・,・・・,DIRC,IRQE,SLIN,IPRT,ATFD,STRB //DIRC:ポート方向の切り替え 1:入力 0:出力 //IRQE:ACK割り込み許可    1:許可 0:禁止 //SLIN:プリンタセレクト制御 1:ON 0:OFF //IPRT:プリンタリセット 1:ON 0:OFF //ATFD:オートフィード 1:ON 0:OFF if(!CanGIVEIO){return;} _outp(control_port,0);// control port 出力モードに _outp(data_port,num);// data port 0~255 } LRESULT CALLBACK WindowProcedure(HWND hWindow,UINT message,WPARAM wParameter,LPARAM lParameter) { switch(message) { case WM_CREATE: hnsSetFullScreenVGA(hWindow); break; case WM_KEYDOWN: switch(wParameter) { //ESCAPEキーをおすとWindowを破壊するWM_DESTROYメッセージを送る case VK_ESCAPE: PostMessage(hWindow,WM_DESTROY,0,0); break; //RETURNキーをおすとタスク開始 case VK_RETURN: hnsTaskLoop(hWindow); hnsSetFullScreenVGA(hWindow); break; } break; case WM_DESTROY: //hnsSetFullScreenVGA(NULL); ない方がいいみたい PostQuitMessage(0); return 0; } return DefWindowProc(hWindow,message,wParameter,lParameter); }