/* デスクトップ・マスコット のサンプル・プログラム その5 Win32 SDK/C&MFC Programming Tips(http://www3.freeweb.ne.jp/misc/kmiwaki/developer/tips/index.shtml)の ・非四角形のウィンドウを作るには?(デスクトップにマスコットを表示させるには?) ・ビットマップの形をしたリージョンを作成するには?(CreateRgnFromBmp) を元にしている。 これは、サンプル4の変更物。 実行すると、bmp絵の形のウィンドウが 現在のアクティブウィンドウに乗っかるようにしてアニメ表示される。 つかむこと可能。右クリックでメニューだして Exit を選んで終了。 また、性質上、多重起動しないようにしている。 ※ アクティブウィンドウの判定が不完全なので、場合によっては   変な位置に表示されてしまう不完全版。 bmp素材提供は, pukaku. 2000/05/20 by tenk* 2000/06/04 ちらつき対策. WM_PAINT中の処理が重たいとちらつきやすい? リージョンの更新をWM_TIMER側に戻したらマシになったかも。 (WM_PAINT中にWM_TIMERが実行される、ということはなさそだった。  WM_PAINTの再入でもないし。疑念で、WM_TIMERとWM_PAINTのやりとりは  mas_anmNoでなくmas_bmpNoに絵の番号経由にし、 各処理頭でmas_anmNo/mas_bmpNoの値は一時変数に移した)。 あと背景描画色を NULL_BRUSH でなく BLACK_BRUSH に変更。ちらついた とき多少マシに見えるかも。 2000/06/04 アクティブウィンドウに寄生する版 */ #include #include "resource.h" /*--------------------------------------------------------------------------*/ static volatile HINSTANCE app_hInst; /* 現在のインスタンス */ static volatile HWND foreground_wnd; /* 現在のフォアグランド窓 */ /* 1コマ、何マイクロ秒か */ #define MICRO_SEC_PER_FRM (8 * (1000/60)) /* 8 フレーム(1/60秒). ちなみに 1000/60≒17ms */ /* マスコットキャラデータの管理用 */ #define MAS_BMP_W (80) // 起動時の窓サイズ. #define MAS_BMP_H (80) // 実際は BMPから取得するので、そのサイズが収まる範囲で適当に設定。 #define MAS_BMP_NUM (sizeof(mas_bmp_res)/sizeof(mas_bmp_res[0])) //絵の枚数(配列より計算) static int mas_bmp_res[] = { IDB_IMAGE_00, /* 0 */ IDB_IMAGE_01, /* 1 */ IDB_IMAGE_02, /* 2 */ }; static HBITMAP mas_hBmp[MAS_BMP_NUM]; static HRGN mas_hRgns[MAS_BMP_NUM]; static volatile HRGN mas_hRgn; static volatile int mas_bmpNo; #define MAS_ANM_NUM (sizeof(mas_anm)/sizeof(mas_anm[0])) //絵を表示する順番(配列より計算) static volatile int mas_anmNo; static int mas_anm[] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 1, 2, 2, 1}; /*--------------------------------------------------------------------------*/ LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); HRGN CreateRgnFromBmp(HBITMAP hBitmap, COLORREF cTransparentColor/* = 0xffffffff*/); /*---------------------------------------------------------------------------*/ /* デバッグ・ログ・ルーチン */ #if 1 //def NDEBUG #define DBGF(n) #else #include #include #define DBGF(n) (dbgf n) static void dbgf(char *fmt, ...) { FILE *fp; va_list app; fp = fopen("dbg.txt", "at"); if (fp) { va_start(app, fmt); vfprintf(fp, fmt, app); va_end(app); fclose(fp); } } #endif /*--------------------------------------------------------------------------*/ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE dummy_hPrevInstance, LPSTR dummy_lpCmdLine, int nCmdShow) { TCHAR szWindowClass[] = "mascot test program -- kotaro"; WNDCLASSEX wcex; HWND hWnd; MSG msg; HANDLE hMutex; RECT rct = {0}; int x0,y0; /* 多重起動防止. */ hMutex = CreateMutex(NULL, 1, szWindowClass); if (GetLastError() == ERROR_ALREADY_EXISTS) return 0; /* このプログラムが起動直前にアクティブだったウィンドウを得る */ foreground_wnd = GetForegroundWindow(); /* BLOCK : ウィンドウ クラスの登録 */ wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = 0; wcex.lpfnWndProc = (WNDPROC) WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInst; wcex.hIcon = 0; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); //wcex.hbrBackground = (HBRUSH)(NULL_BRUSH); wcex.hbrBackground = (HBRUSH)(BLACK_BRUSH); wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = NULL; RegisterClassEx(&wcex); #if 1 // 窓に寄生する GetWindowRect(hWnd, &rct); x0 = rct.left + 20; y0 = rct.top - MAS_BMP_H + 7; #else // 画面中心に表示するとき //SystemParametersInfo(SPI_GETWORKAREA, 0, &rct, 0); //x0 = (rct.right - MAS_BMP_W) / 2; //y0 = (rct.bottom - MAS_BMP_H) / 2; #endif /* メインウィンドウの作成 */ app_hInst = hInst; /* グローバル変数にインスタンス ハンドルの保存 */ hWnd = CreateWindowEx( /* WS_EX_TOPMOST |*/ WS_EX_TOOLWINDOW , szWindowClass , NULL , WS_POPUP|WS_MINIMIZE , x0 , y0 , MAS_BMP_W , MAS_BMP_H , NULL , NULL , hInst , NULL ); if (hWnd == 0) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); /* メイン メッセージ ループ */ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } ReleaseMutex(hMutex); return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: { HRGN hRgn_tmp; RECT rect; int i,n; GetClientRect(hWnd, &rect); for (i = 0; i < MAS_BMP_NUM; i++) { mas_hBmp[i] = LoadBitmap(app_hInst, MAKEINTRESOURCE(mas_bmp_res[i])); mas_hRgns[i] = CreateRgnFromBmp(mas_hBmp[i], (UINT)-1); } hRgn_tmp = CreateRectRgn(0, 0, 0, 0); mas_anmNo = 0; mas_bmpNo = mas_anm[mas_anmNo]; CombineRgn(hRgn_tmp, mas_hRgns[mas_bmpNo], NULL, RGN_COPY); SetWindowRgn(hWnd, hRgn_tmp, 0); mas_hRgn = hRgn_tmp; SetProp(hWnd, "region", mas_hRgn); } /* タイマー割り込み設定. */ SetTimer(hWnd,1, MICRO_SEC_PER_FRM, NULL); break; case WM_TIMER: /* 単位時間ごとの処理. */ { // ウィンドウの移動 static int tmr = 0; // ゆれ処理用 int n = (tmr++) % 12; // 〃 int d = (n < 6) ? n : 11-n; // 〃 RECT r; if (GetWindowRect(hWnd, &r)) { RECT ar; int x = r.left, y = r.top; HWND actHwnd = GetForegroundWindow(); if (actHwnd == hWnd) { // 自分自身が最前列だったらば、その直前のウィンドウにする while (foreground_wnd == NULL || IsWindow(foreground_wnd) == 0) { // 直前窓が有効?ちがえば... foreground_wnd = GetNextWindow(actHwnd, GW_HWNDNEXT); // 適当に次のウィンドウを選ぶ if (foreground_wnd == NULL) { // 適当なウィンドウが無かったらば foreground_wnd = hWnd; // そのままの位置にする break; } actHwnd = foreground_wnd; } actHwnd = foreground_wnd; // その直前窓/選択した窓を設定 } if (actHwnd && actHwnd != hWnd && GetWindowRect(actHwnd, &ar)) { // 寄生すべき窓が有効ならば foreground_wnd = actHwnd; x = ar.left; y = ar.top; if (x > 0 || y > 0) { // 座標が表示可能か? x = ar.left+20; y = ar.top-MAS_BMP_H+7 + d/2; } else { // 中途半端になりそうならば、強引に画面外に設定 x = -MAS_BMP_W; y = -MAS_BMP_H; } } SetWindowPos(hWnd, HWND_TOPMOST, x, y, r.right-r.left, r.bottom-r.top, SWP_NOACTIVATE|SWP_SHOWWINDOW); //MoveWindow(hWnd, x, y, r.right-r.left, r.bottom-r.top, TRUE); } } { // 画面更新 int cgNoOld, cgNoNew; HRGN hRgn_tmp; cgNoOld = mas_anm[mas_anmNo]; mas_anmNo = (mas_anmNo + 1) % MAS_ANM_NUM; // アニメ番号を更新 cgNoNew = mas_anm[mas_anmNo]; if (cgNoNew != cgNoOld) { // 前回と絵が違えば更新 // リージョンの設定 hRgn_tmp = CreateRectRgn(0, 0, 0, 0); CombineRgn(hRgn_tmp, mas_hRgns[cgNoNew], NULL, RGN_COPY); //CombineRgn(hRgn_tmp, hRgn_tmp, mas_hRgns[cgNoNew], RGN_OR); SetWindowRgn(hWnd, hRgn_tmp, TRUE); if (mas_hRgn) DeleteObject(mas_hRgn); mas_hRgn = hRgn_tmp; SetProp(hWnd, "region", mas_hRgn); InvalidateRect(hWnd, NULL, 0); // 画面更新要求。WM_PAINTが実行される。 } mas_bmpNo = cgNoNew; // 次回描画する絵の番号を設定 } break; case WM_PAINT: { PAINTSTRUCT ps; HRGN hRgn_tmp; BITMAP bm; // 画像のヘッダ情報取得用 HDC dst_hdc; HDC src_hdc; int n; n = mas_bmpNo; // n = mas_anm[mas_anmNo]; GetObject(mas_hBmp[n], sizeof(BITMAP), &bm); dst_hdc = BeginPaint(hWnd, &ps); // 描画開始 { // bmp を描画 src_hdc = CreateCompatibleDC(dst_hdc); SelectObject(src_hdc, mas_hBmp[n]); BitBlt(dst_hdc, 0,0, bm.bmWidth, bm.bmHeight, src_hdc, 0,0, SRCCOPY); } EndPaint(hWnd, &ps); // 描画終了 DeleteDC(src_hdc); } break; case WM_DESTROY: //終了 { int i; for (i = 0; i < MAS_BMP_NUM; i++) { if (mas_hBmp[i]) DeleteObject(mas_hBmp[i]); if (mas_hRgns[i]) DeleteObject(mas_hRgns[i]); } // リージョンの解放 if (GetProp(hWnd, "region")) { if (mas_hRgn) DeleteObject(mas_hRgn); DeleteObject(GetProp(hWnd, "region")); RemoveProp(hWnd, "region"); } } KillTimer(hWnd,1); /* タイマー割り込み終了 */ PostQuitMessage(0); /* 終了リクエスト */ break; case WM_NCRBUTTONDOWN: { // 右ボタンが押されたときの処理。ユーザ終了ができるようメニューを出す。 int xPos, yPos; HMENU hMenu, hSubMenu; xPos = LOWORD(lParam); yPos = HIWORD(lParam); hMenu = LoadMenu(app_hInst, MAKEINTRESOURCE(IDC_MAINMENU)); hSubMenu = GetSubMenu(hMenu, 0); TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, xPos, yPos, 0, hWnd, NULL); } break; case WM_COMMAND: { /* メニュー選択処理 */ //int wmEvent = HIWORD(wParam); int wmId = LOWORD(wParam); switch (wmId) { case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } } break; case WM_NCHITTEST: /* 窓をつかんだとき、あたかもタイトルバーにマウスがあるようにWindowsを騙す */ wParam = DefWindowProc(hWnd, uMsg, wParam, lParam); if (wParam == HTCLIENT) return HTCAPTION; return wParam; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } /*--------------------------------------------------------------------------*/ /* 関数 : CreateRgnFromBmp ( HBITMAP, COLORREF ) */ /* 用途 : ビットマップからリージョンを作成する */ /* 引数 : */ /* hBitmap - リージョンを作成するビットマップ */ /* cTransparentColor - 透明色(RGB(255,255,255)のように指定する) */ /* デフォルトでは左下隅のドットの色が透明色となる */ HRGN CreateRgnFromBmp(HBITMAP hBitmap, COLORREF cTransparentColor/* = 0xffffffff*/) { BITMAP bm; /* BITMAP structure */ HBITMAP hDIBSection = NULL; /* Handle to DIB */ COLORREF *pBits = NULL; /* Pointer to the DIB's bit values */ HRGN hRgn; /* Handle to Region */ /* hBitmap のチェック */ if (hBitmap == 0) return NULL; /* ビットマップのサイズを取得 */ if (GetObject(hBitmap, sizeof(bm), &bm) == 0) return NULL; /* BLOCK : DIBの作成 */ /* 成功すると hDIBSection にビットマップハンドル、pBits にビットへの */ /* ポインタが設定される。(DeleteObject(hDIBSection); を忘れてはいけない) */ /* 失敗すると hDIBSection 及び pBits は NULL のまま。 */ { HDC hMemDC; /* Handle to a device context : DIB */ HDC hCopyDC = NULL; /* Handle to a device context : DDB -> DIB */ HBITMAP hOldDIBsBmp = NULL; /* Handle of the object being replaced : DIB */ HBITMAP hOldDDBsBmp = NULL; /* Handle of the object being replaced : DDB */ BITMAPINFOHEADER bmih; /* DIB's BITMAPINFOHEADER structure */ BOOL bResultBitBlt = TRUE; /* Result of BitBlt function */ /* メモリデバイスコンテキストを作成 */ hMemDC = CreateCompatibleDC(NULL); if (hMemDC == NULL) goto CLEANUP_DIB; /* DIB(Device Independent Bitmaps : デバイスに依存しないビットマップ) */ /* のヘッダ情報を書きこむ */ bmih.biSize = sizeof(BITMAPINFOHEADER), /* size of the structure */ bmih.biWidth = bm.bmWidth; /* width of the bitmap */ bmih.biHeight = bm.bmHeight; /* height of the bitmap */ bmih.biPlanes = 1; /* must be set to 1 */ bmih.biBitCount = 32; /* maximum of 2^32 colors */ bmih.biCompression = BI_RGB; /* an uncompressed format */ bmih.biSizeImage = 0; /* must be set to zero for BI_RGB bitmaps */ bmih.biXPelsPerMeter = 0; /* set to zero */ bmih.biYPelsPerMeter = 0; /* set to zero */ bmih.biClrUsed = 0; /* must be set to zero */ bmih.biClrImportant = 0; /* set to zero, all colors are required */ /* DIBの作成 */ hDIBSection = CreateDIBSection( hMemDC, /* HDC hdc : handle to device context */ (CONST BITMAPINFO *)&bmih, /* CONST BITMAPINFO *pbmi : pointer to BITMAPINFO structure */ DIB_RGB_COLORS, /* UINT iUsage : color data type(RGB values) */ (void **) &pBits, /* VOID *ppvBits : pointer to receive the bitmap's bit values */ NULL, /* HANDLE hSection : handle to a file mapping object. */ /* This parameter can be NULL. */ 0 /* DWORD dwOffset : ignored by hSection */ ); if (hDIBSection == NULL) goto CLEANUP_DIB; /* 作成したDIBをDCに選択 */ hOldDIBsBmp = (HBITMAP) SelectObject(hMemDC, hDIBSection); /* 画像の転送元となるメモリデバイスコンテキストを作成 */ hCopyDC = CreateCompatibleDC(hMemDC); if (hCopyDC == NULL) goto CLEANUP_DIB; /* hBitmap で指定されたビットマップをデバイスコンテキストに選択 */ hOldDDBsBmp = (HBITMAP) SelectObject(hCopyDC, hBitmap); /* ビットマップをhMemDCに転送 */ bResultBitBlt = BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hCopyDC, 0, 0, SRCCOPY); /* クリーンアップ */ CLEANUP_DIB: if (hOldDDBsBmp) SelectObject(hCopyDC, hOldDDBsBmp); if (hCopyDC) DeleteDC(hCopyDC); if (hOldDIBsBmp) SelectObject(hMemDC, hOldDIBsBmp); if (hMemDC) DeleteDC(hMemDC); /* BitBlt 関数が失敗していたらDIBを削除しNULLに設定 */ if (bResultBitBlt == 0) { DeleteObject(hDIBSection); hDIBSection = NULL; pBits = NULL; } } /* End of BLOCK : DIBの作成 */ /* DIBの作成に失敗していたらリージョンは作れない */ if (hDIBSection == NULL) return NULL; /* BLOCK : リージョンの作成 */ { HRGN hDotRgn; /* 一時的なドットのリージョン */ int x,y,xx; /* 透明色が指定されていなければ、左下隅のドットの色が透明色となる */ if (cTransparentColor == 0xffffffff) cTransparentColor = *pBits; /* 空のリージョンの作成 */ hRgn = CreateRectRgn(0, 0, 0, 0); /* 縦横にビットが透明色かを調べ、透明色で無い部分をリージョンに追加 */ for (y = 0; y < bm.bmHeight; y++) { /* 縦方向 */ for (x = 0; x < bm.bmWidth; x++) { /* 横方向 */ if (*pBits != cTransparentColor) { /* 透明色ではない? *//* Yes! */ xx = x; /* x を保存(透明色ではない点の始まりを保存) */ /* 連続した透明色ではない領域を求める */ for (x = x + 1; x < bm.bmWidth; x++) { /* ポインタをインクリメント */ pBits++; /* 透明色になったら break */ if (*pBits == cTransparentColor) break; } /* 一時的な透明色でない領域のリージョンを作成 */ hDotRgn = CreateRectRgn(xx, bm.bmHeight - y, x, bm.bmHeight - y - 1); /* hRgn と合成(ビットマップ全体のリージョンを作成) */ CombineRgn(hRgn, hRgn, hDotRgn, RGN_OR); /* 一時的なリージョンを削除 */ DeleteObject(hDotRgn); } /* ポインタをインクリメント */ pBits++; } } } /* End of BLOCK : リージョンの作成 */ /* HBITMAP(DIB)を削除 */ DeleteObject(hDIBSection); /* return the handle of the region */ return hRgn; }