OSDN Git Service

Windows APIを実装部に隠蔽してみた。まだ不完全だが。
[wintimer/wintimer.git] / wintimer / toplevel_window.cpp
1 #include "stdafx.h"
2 #define BOOST_ASSIGN_MAX_PARAMS 7
3 #include <boost/assign.hpp>
4 #include <boost/assign/ptr_list_of.hpp>
5 #include <boost/assign/ptr_list_inserter.hpp>
6 #include <boost/foreach.hpp>
7 #include "toplevel_window.h"
8 #include "sf_windows.h"
9 #include "exception.h"
10
11 #define THROW_IFERR(hres) \
12   if (FAILED(hres)) { throw sf::win32_error_exception(hres); }
13
14 #ifndef HINST_THISCOMPONENT
15 EXTERN_C IMAGE_DOS_HEADER __ImageBase;
16 #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
17 #endif
18
19 namespace sf 
20 {
21
22 HRESULT EnableBlurBehind(HWND hwnd)
23 {
24   HRESULT hr = S_OK;
25
26   //Create and populate the BlurBehind structre
27   DWM_BLURBEHIND bb = {0};
28   //Enable Blur Behind and Blur Region;
29   bb.dwFlags = DWM_BB_ENABLE;
30   bb.fEnable = true;
31   bb.hRgnBlur = NULL;
32
33   //Enable Blur Behind
34   hr = DwmEnableBlurBehindWindow(hwnd, &bb);
35   if (SUCCEEDED(hr))
36   {
37     //do more things
38   }
39   return hr;
40 }
41  
42 struct toplevel_window::impl : public base_win32_window
43 {
44
45   impl(const std::wstring& menu_name,const std::wstring& name,bool fit_to_display,float width = 160,float height = 100) 
46     : base_win32_window(menu_name,name,fit_to_display,width,height) , wm_task_bar_create_(0),timer_id_(0),result_time_(INTERVAL_SEC1),status_(active)
47   {
48     on_render.connect(boost::bind(&impl::render,this));
49   };
50
51   ~impl(){
52     safe_release(factory_);
53     safe_release(write_factory_);
54   };
55
56   LRESULT window_proc(HWND hwnd,boost::uint32_t message, WPARAM wParam, LPARAM lParam){
57     switch (message)
58     {
59     case WM_CREATE:
60       {
61         // TODO:
62         create_device();
63         wm_task_bar_create_ = RegisterWindowMessage(L"TaskbarButtonCreated");
64         text(std::wstring(L"ミニタイマー(活動中)"));
65         //MARGINS mgn = {-1,0,0,0};//let,right,top,bottom
66         //HRESULT hr = DwmExtendFrameIntoClientArea(hwnd_, &mgn);
67         break;
68       }
69     case WM_SIZE:
70       {
71         //if (render_target_)
72         //{
73         //      D2D1_SIZE_U size;
74         //      size.width = lParam & 0xFFFF;
75         //      size.height = (lParam >> 16) & 0xFFFF; ;
76
77         //      // Note: This method can fail, but it's okay to ignore the
78         //      // error here -- it will be repeated on the next call to
79         //      // EndDraw.
80         //      render_target_->Resize(size);
81         //}
82       }
83     case WM_PAINT:
84       {
85         //create_device();
86
87         { 
88
89           paint_struct begin_paint(hwnd);
90         
91           //CloseHandle(cb);
92           // 描画コードの呼び出し
93           render();
94
95         }
96
97 //        ::ShowWindow(hwnd_,SW_MINIMIZE);
98         return FALSE;
99       }
100     case WM_DISPLAYCHANGE:
101       {
102         ::InvalidateRect(hwnd, NULL, FALSE);
103       }
104       break;
105     case WM_ERASEBKGND:
106       {
107 //        return FALSE;
108       }
109       break;
110     case WM_MOUSEMOVE:
111       {
112         //                                      on_mouse_move(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),wParam); 
113       }
114       break;
115     case WM_LBUTTONDOWN:
116       {
117       }
118       break;
119     case WM_TIMER:
120       {
121         switch (status_)
122         {
123         case active:
124           --result_time_;
125           taskbar_.progress_value(*this,result_time_,INTERVAL_SEC1);
126
127           if(result_time_ == 0)
128           {
129             ::MessageBeep(MB_ICONEXCLAMATION);
130             status_ = sleep;
131             result_time_ = INTERVAL_SEC2;
132             taskbar_.progress_state(*this,taskbar::paused);
133             taskbar_.progress_value(*this,result_time_,INTERVAL_SEC2);
134             text(std::wstring(L"ミニタイマー(休憩中)"));
135           }
136             ::InvalidateRect(hwnd_,NULL,FALSE);
137           break;
138         case sleep:
139           {
140             --result_time_;
141             taskbar_.progress_value(*this,result_time_,INTERVAL_SEC2);
142             if(result_time_ == 0)
143             {
144               ::MessageBeep(MB_OK);
145               status_ = active;
146               result_time_ = INTERVAL_SEC1;
147               taskbar_.progress_state(*this,taskbar::indeterminate);
148               taskbar_.progress_value(*this,result_time_,INTERVAL_SEC1);
149               text(std::wstring(L"ミニタイマー(活動中)"));
150             }
151             invalidate_rect();
152           }
153           break;
154         }
155     case WM_DWMCOMPOSITIONCHANGED:
156       {
157         //MARGINS mgn = {-1,0,0,0};//let,right,top,bottom
158         //HRESULT hr = DwmExtendFrameIntoClientArea(hwnd_, &mgn);
159       }
160       break;
161         //::SetTimer(hwnd_,(UINT_PTR)&timer_id_,1000,NULL);
162       }
163     }
164
165     if(message == wm_task_bar_create_)
166     {
167       taskbar_.create();
168       taskbar_.overlay_icon(*this,NULL,std::wstring(L"ミニタイマー"));
169       taskbar_.progress_state(*this,taskbar::indeterminate);
170       taskbar_.progress_value(*this,100,100);
171       // タイマーの設定
172       ::SetTimer(hwnd_,(UINT_PTR)&timer_id_,1000,NULL);
173     }
174
175     if(message == WM_CLOSE)
176     {
177       ::KillTimer(hwnd_,(UINT_PTR)&timer_id_);
178       //
179       taskbar_.discard();
180       // 後始末
181       discard_device();
182       // レンダーターゲットのリリース
183       safe_release(render_target_);
184       // Windowの破棄
185       BOOL ret(::DestroyWindow(hwnd));
186       BOOST_ASSERT(ret != 0);
187     }
188
189     if(message == WM_DESTROY)
190     {
191       ::PostQuitMessage(0);
192       return 0;
193     }
194
195     return ::DefWindowProcW(hwnd,message,wParam,lParam);
196   }
197
198   virtual void create(){
199     create_device_independent_resources();
200     register_class();
201     create_window();
202
203     // 半透明ウィンドウを有効にする。
204     BOOL dwmEnable;
205     DwmIsCompositionEnabled (&dwmEnable); 
206     if (dwmEnable) EnableBlurBehind(*this);
207
208   };
209
210   virtual void discard_device()
211   {
212     safe_release(render_target_);
213   }
214
215   virtual void create_device(){
216     //          入力_.reset(new input(HINST_THISCOMPONENT,hwnd_));
217     HRESULT hr = S_OK;
218
219     //ウィンドウの現在の幅、高さを求める
220     RECT rc;
221     GetClientRect( hwnd_, &rc );
222     boost::uint32_t width = rc.right - rc.left;
223     boost::uint32_t height = rc.bottom - rc.top;
224
225     {
226       //wic_imaging_factory_.CreateInstance(CLSID_WICImagingFactory);
227       //                        bitmap_ = load_bitmap_from_file(render_target_,wic_imaging_factory_,L"myship.png");
228     }
229
230     if(!render_target_)
231     {
232       RECT rc;
233       GetClientRect(hwnd_, &rc);
234
235       D2D1_SIZE_U size = D2D1::SizeU(
236         rc.right - rc.left,
237         rc.bottom - rc.top
238         );
239       
240       const D2D1_PIXEL_FORMAT format =
241           D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
242                             D2D1_ALPHA_MODE_PREMULTIPLIED);
243       
244       const D2D1_RENDER_TARGET_PROPERTIES target_prop = 
245           D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,format);
246
247       THROW_IFERR(factory_->CreateHwndRenderTarget(
248         target_prop,
249         D2D1::HwndRenderTargetProperties(hwnd_, size,D2D1_PRESENT_OPTIONS_IMMEDIATELY),
250         &render_target_
251         ));
252       // Create a DC render target 
253       //D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
254       //        D2D1_RENDER_TARGET_TYPE_DEFAULT,
255       //        D2D1::PixelFormat(
256       //                DXGI_FORMAT_B8G8R8A8_UNORM,
257       //                D2D1_ALPHA_MODE_IGNORE
258       //                ) , 0.0, 0.0,
259       //        D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE
260       //        );
261
262       //THROW_IFERR(factory_->CreateDCRenderTarget(
263       //        &props,
264       //        &render_target_
265       //        ));
266     }
267   }
268
269   virtual void create_device_independent_resources(){
270     // Direct2DFactory の生成
271
272     if(!factory_){
273 #if defined(DEBUG) || defined(_DEBUG)
274       D2D1_FACTORY_OPTIONS options;
275       options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION ;
276       THROW_IFERR(D2D1CreateFactory(
277         D2D1_FACTORY_TYPE_SINGLE_THREADED,
278         options,
279         &factory_
280         ));
281 #else
282       THROW_IFERR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory_));
283 #endif
284
285     }
286
287     if(!write_factory_){
288       THROW_IFERR(::DWriteCreateFactory(
289         DWRITE_FACTORY_TYPE_SHARED,
290         __uuidof(IDWriteFactory),
291         reinterpret_cast<IUnknown**>(&write_factory_)
292         ));
293     }
294
295
296     //wic_imaging_factory_.CreateInstance(CLSID_WICImagingFactory);
297
298     //thunk_proc_ = (WNDPROC)thunk_.getCode();
299     layout_rect_ = D2D1::RectF(0.0f,0.0f,width_,height_);
300     // Text Formatの作成
301     THROW_IFERR(write_factory_->CreateTextFormat(
302     L"メイリオ",                // Font family name.
303     NULL,                       // Font collection (NULL sets it to use the system font collection).
304     DWRITE_FONT_WEIGHT_REGULAR,
305     DWRITE_FONT_STYLE_NORMAL,
306     DWRITE_FONT_STRETCH_NORMAL,
307     24.0f,
308     L"ja-jp",
309     &write_text_format_
310     ));
311
312   }
313
314   void render(){
315     static float t = 0.0f;
316
317     if (render_target_)
318     {
319       // Retrieve the size of the render target.
320       D2D1_SIZE_F renderTargetSize = render_target_->GetSize();
321       try {
322         render_target_->BeginDraw();
323         render_target_->Clear(D2D1::ColorF(0,0.0f));
324 //        render_target_->FillRectangle(D2D1::RectF(0.0f,0.0f,renderTargetSize.width,renderTargetSize.height),);
325          
326 //          (D2D1::ColorF(0,1.0f));
327         render_target_->SetTransform(D2D1::Matrix3x2F::Identity());
328         ID2D1SolidColorBrushPtr brush;
329         render_target_->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::OrangeRed), &brush);
330
331         std::wstring m((boost::wformat(L"残り%d分%d秒") % (result_time_ / 60) % (result_time_ % 60)).str());
332         render_target_->DrawTextW(
333           m.c_str(),
334           m.size(),
335           write_text_format_,
336           layout_rect_, 
337           brush);
338         THROW_IFERR(render_target_->EndDraw());
339       } catch(sf::win32_error_exception& err)
340       {
341         if(err.hresult() == D2DERR_RECREATE_TARGET)
342         {
343           discard_device();
344           create_device();
345         } else {
346           throw;
347         }
348       } catch(...) {
349         throw;
350       }
351     }
352   }
353   void create_window()
354   {
355
356     // Windowを作成する
357     CreateWindowEx(
358       WS_EX_APPWINDOW | WS_EX_TOPMOST,
359       name_.c_str(),
360       title_.c_str(),
361       0 ,
362       CW_USEDEFAULT,
363       CW_USEDEFAULT,
364       static_cast<boost::uint32_t>(ceil(width_ /** dpiX / 96.f*/)),
365       static_cast<boost::uint32_t>(ceil(height_ /** dpiY / 96.f*/)),
366       NULL,
367       NULL,
368       HINST_THISCOMPONENT,
369       this
370       );
371   };
372
373 private:
374
375   long wm_task_bar_create_;
376   sf::taskbar taskbar_;
377   UINT timer_id_;
378   static const int INTERVAL_SEC1 = 60 * 15;// 15分
379   static const int INTERVAL_SEC2 = 30;// 30秒
380
381   int result_time_;
382   enum timer_status {
383     active,
384     sleep
385   };
386
387   ID2D1FactoryPtr factory_;
388   ID2D1HwndRenderTargetPtr render_target_;
389   IDWriteFactoryPtr write_factory_;
390   IWICImagingFactoryPtr wic_imaging_factory_;
391
392   timer_status status_;
393   IDWriteTextFormatPtr write_text_format_;
394   D2D1_RECT_F layout_rect_;
395 };
396
397   //
398   // Creates a Direct2D bitmap from the specified
399   // file name.
400   //
401   ID2D1BitmapPtr load_bitmap_from_file(
402     ID2D1HwndRenderTargetPtr render_target,
403     IWICImagingFactoryPtr wic_factory,
404     std::wstring uri,
405     boost::uint32_t destination_width,
406     boost::uint32_t destination_height
407     )
408   {
409     HRESULT hr = S_OK;
410
411     IWICBitmapDecoderPtr decoder;
412     IWICBitmapFrameDecodePtr decoder_source;
413     IWICStreamPtr stream;
414     IWICFormatConverterPtr converter;
415     IWICBitmapScalerPtr scaler;
416     ID2D1BitmapPtr bitmap;
417
418     THROW_IFERR(wic_factory->CreateDecoderFromFilename(
419       uri.c_str(),
420       NULL,
421       GENERIC_READ,
422       WICDecodeMetadataCacheOnLoad,
423       &decoder
424       ));
425
426     // Create the initial frame.
427     THROW_IFERR(decoder->GetFrame(0, &decoder_source));
428
429     // Convert the image format to 32bppPBGRA
430     // (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
431     THROW_IFERR(hr = wic_factory->CreateFormatConverter(&converter));
432
433     // If a new width or height was specified, create an
434     // IWICBitmapScaler and use it to resize the image.
435     if (destination_width != 0 || destination_height != 0)
436     {
437       boost::uint32_t originalWidth, originalHeight;
438       THROW_IFERR(decoder_source->GetSize((UINT*)&originalWidth, (UINT*)&originalHeight));
439       if (destination_width == 0)
440       {
441         FLOAT scalar = static_cast<FLOAT>(destination_height) / static_cast<FLOAT>(originalHeight);
442         destination_width = static_cast<boost::uint32_t>(scalar * static_cast<FLOAT>(originalWidth));
443       }
444       else if (destination_height == 0)
445       {
446         FLOAT scalar = static_cast<FLOAT>(destination_width) / static_cast<FLOAT>(originalWidth);
447         destination_height = static_cast<boost::uint32_t>(scalar * static_cast<FLOAT>(originalHeight));
448       }
449
450       THROW_IFERR(wic_factory->CreateBitmapScaler(&scaler));
451       THROW_IFERR(scaler->Initialize(
452         decoder_source,
453         destination_width,
454         destination_height,
455         WICBitmapInterpolationModeCubic
456         ));
457       THROW_IFERR(converter->Initialize(
458         scaler.GetInterfacePtr(),
459         GUID_WICPixelFormat32bppPBGRA,
460         WICBitmapDitherTypeNone,
461         NULL,
462         0.f,
463         WICBitmapPaletteTypeMedianCut
464         ));
465     }
466     else // Don't scale the image.
467     {
468       THROW_IFERR(converter->Initialize(
469         decoder_source.GetInterfacePtr(),
470         GUID_WICPixelFormat32bppPBGRA,
471         WICBitmapDitherTypeNone,
472         NULL,
473         0.f,
474         WICBitmapPaletteTypeMedianCut
475         ));
476     }
477
478     // Create a Direct2D bitmap from the WIC bitmap.
479     THROW_IFERR(render_target->CreateBitmapFromWicBitmap(
480       converter.GetInterfacePtr(),
481       NULL,
482       &bitmap
483       ));
484
485     return bitmap;
486   }
487
488   toplevel_window::toplevel_window(const std::wstring& menu_name,const std::wstring& name,bool fit_to_display,float width ,float height)
489     : impl_(new impl(menu_name,name,fit_to_display,width,height))
490   {
491
492   };
493
494   void * toplevel_window::raw_handle(){return impl_->raw_handle();};
495   void toplevel_window::create(){impl_->create();};
496   void toplevel_window::show(boost::uint32_t show_flag){impl_->show(show_flag);};
497   void toplevel_window::text(std::wstring& text){impl_->text(text);};
498   void toplevel_window::update(){impl_->update();};
499
500  toplevel_window_ptr create_toplevel_window
501     (
502     const std::wstring& menu_name,
503     const std::wstring& name,
504     const boost::uint32_t show_flag,
505     bool fit_to_display,
506     float width,
507     float height
508     )
509   {
510     toplevel_window* p = new toplevel_window(menu_name,name,fit_to_display,width,height);
511     p->create();
512     p->show(show_flag);
513     p->update();
514     return toplevel_window_ptr(p);
515   }
516   
517 }
518