OSDN Git Service

78cdc6d61620cce8c6c318a9c127c648542826a2
[gvonavish/GVONavish.git] / GVONavish / GVONavish / GVONavish.cpp
1 #include "stdafx.h"
2 #include <vector>
3 #include <list>
4
5 // PNG\82Æ\82©\82Ì\93Ç\82Ý\8d\9e\82Ý\97p
6 #include <gdiplus.h>
7 #pragma comment(lib, "gdiplus.lib")
8
9 // PATH API\97p
10 #include <Shlwapi.h>
11 #pragma comment(lib, "shlwapi.lib")
12
13 #include <CommCtrl.h>
14 #pragma comment(lib, "comctl32.lib")
15 #include <CommDlg.h>
16 #pragma comment(lib, "Comdlg32.lib")
17
18
19
20 #include "GVONavish.h"
21 #include "GVOConfig.h"
22 #include "GVOGameProcess.h"
23 #include "GVOWorldMap.h"
24 #include "GVOShip.h"
25 #include "GVOShipRouteList.h"
26 #include "GVORenderer.h"
27 #include "GVOTexture.h"
28 #include "GVOShipRouteManageView.h"
29
30
31 // \83f\83o\83b\83O\8e\9e\82Ì\95`\89æ\83p\83t\83H\81[\83}\83\93\83X\91ª\92è\97p\81B
32 //#define GVO_PERF_CHECK
33
34
35
36 // \83O\83\8d\81[\83o\83\8b\95Ï\90\94:
37 HINSTANCE g_hinst;                                                                              // \8c»\8dÝ\82Ì\83C\83\93\83^\81[\83t\83F\83C\83X
38 HWND g_hwndMain;
39 HDC g_hdcMain;
40
41
42 // \8aÖ\90\94\83v\83\8d\83g\83^\83C\83v\90é\8c¾
43 static ATOM MyRegisterClass( HINSTANCE hInstance );
44 static BOOL InitInstance( HINSTANCE, int );
45 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
46 BOOL CALLBACK aboutDlgProc( HWND, UINT, WPARAM, LPARAM );
47 static LRESULT s_mainLoop();
48
49
50 // \83\81\83b\83Z\81[\83W\83n\83\93\83h\83\89
51 static bool s_onCreate( HWND, LPCREATESTRUCT );
52 static void s_onMove( HWND, WORD, WORD );
53 static void s_onSize( HWND, UINT, WORD, WORD );
54 static void s_onMouseWheel( HWND, int16_t, UINT, int16_t, int16_t );
55 static void s_onMouseMove( HWND, UINT, int16_t, int16_t );
56 static void s_onMouseLeftButtonDown( HWND, UINT, int16_t, int16_t );
57 static void s_onMouseLeftButtonUp( HWND, UINT, int16_t, int16_t );
58 static void s_onMouseLeftButtonDoubleClick( HWND, UINT, int16_t, int16_t );
59 static void s_onMouseRightButtonUp( HWND, UINT, int16_t, int16_t );
60 static void s_onPaint( HWND );
61
62
63 // \83A\83v\83\8a\8f\88\97\9d
64 static std::wstring s_getMapFileName();
65 static void s_updateFrame(HWND);
66 static void s_updateWindowTitle( HWND, POINT, double );
67 static void s_toggleKeepForeground( HWND );
68 static void s_popupMenu( HWND, int16_t, int16_t );
69 static void s_popupCoord( HWND, int16_t, int16_t );
70 static void s_closeShipRoute();
71
72
73 // \83\8d\81[\83J\83\8b\95Ï\90\94
74 static LPCWSTR const k_appName = L"GVONavish";          // \83A\83v\83\8a\83P\81[\83V\83\87\83\93\96¼
75 static LPCWSTR const k_version = L"ver 1.3";            // \83o\81[\83W\83\87\83\93\94Ô\8d\86
76 static LPCWSTR const k_copyright = L"copyright(c) 2014 @MandhelingFreak";       // \92\98\8dì\8c \95\\8e¦\81i\82¢\82¿\82¨\81[\81j
77
78 static LPCWSTR const k_windowClassName = L"GVONavish";          // \83\81\83C\83\93 \83E\83B\83\93\83h\83\83N\83\89\83X\96¼
79 static const LPCWSTR k_configFileName = L"GVONavish.ini";       // \90Ý\92è\83t\83@\83C\83\8b\96¼
80 static LPCWSTR const k_appMutexName = L"Global\\{7554E265-3247-4FCA-BC60-5AA814658351}";
81 static HANDLE s_appMutex;
82
83 static Gdiplus::GdiplusStartupInput s_gdisi;
84 static ULONG_PTR s_gdiToken;
85
86 static GVOConfig s_config( k_configFileName );
87 static GVOGameProcess s_gvoGameProcess;
88 static GVORenderer s_renderer;
89 static GVOWorldMap s_worldMap;
90 static std::unique_ptr<GVOShipRouteList> s_shipRouteList;
91 static POINT s_latestSurveyCoord;
92 static GVOVector s_latestShipVector;
93 static double s_latestShipVelocity;
94 static DWORD s_latestTimeStamp;
95 static const DWORD k_surveyCoordLostThreshold = 5000;
96 static std::unique_ptr<GVOShipRouteManageView> s_shipRouteManageView;
97
98 //\88ê\8e\9e\92u\82«
99 static std::unique_ptr<GVOTexture> s_shipTexture;
100
101 static UINT s_pollingInterval = 1000;   // \8fó\91Ô\8aÄ\8e\8b\8aÔ\8au\81i1\95b\81j
102 static bool s_isDragging = false;               // \83h\83\89\83b\83O\8fó\91Ô\83t\83\89\83O
103 static SIZE s_clientSize;                               // \83N\83\89\83C\83A\83\93\83g\97Ì\88æ\82Ì\91å\82«\82³
104 static POINT s_dragOrg;                                 // \83h\83\89\83b\83O\8c´\93_\81i\88Ú\93®\97Ê\8eZ\8fo\97p\81j
105
106 #ifdef GVO_PERF_CHECK
107 typedef std::deque<double> PerfCountList;
108 static PerfCountList s_perfCountList;
109 #endif
110
111
112
113
114
115 int APIENTRY _tWinMain( _In_ HINSTANCE hInstance,
116         _In_opt_ HINSTANCE hPrevInstance,
117         _In_ LPTSTR    lpCmdLine,
118         _In_ int       nCmdShow )
119 {
120         UNREFERENCED_PARAMETER( hPrevInstance );
121         UNREFERENCED_PARAMETER( lpCmdLine );
122
123         ::SetLastError( NOERROR );
124         s_appMutex = ::CreateMutex( NULL, TRUE, k_appMutexName );
125         if ( ::GetLastError() == ERROR_ALREADY_EXISTS ) {
126                 HWND hwnd = ::FindWindow( k_windowClassName, NULL );
127                 if ( hwnd ) {
128                         ::SetForegroundWindow( hwnd );
129                 }
130                 return 0;
131         }
132
133         ::CoInitialize( NULL );
134         TIMECAPS tc;
135         ::timeGetDevCaps( &tc, sizeof(tc) );
136         ::timeBeginPeriod( tc.wPeriodMin );
137         INITCOMMONCONTROLSEX icc = { sizeof(icc), ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES };
138         ::InitCommonControlsEx( &icc );
139         GdiplusStartup( &s_gdiToken, &s_gdisi, NULL );
140         s_config.load();
141
142         MyRegisterClass( hInstance );
143
144         // \83A\83v\83\8a\83P\81[\83V\83\87\83\93\82Ì\8f\89\8aú\89»\82ð\8eÀ\8ds\82µ\82Ü\82·:
145         if ( !InitInstance( hInstance, nCmdShow ) ) {
146                 return 0;
147         }
148
149         // \83\81\83C\83\93 \83\81\83b\83Z\81[\83\83\8b\81[\83v:
150         const LRESULT retVal = s_mainLoop();
151
152         s_gvoGameProcess.teardown();
153
154         s_config.save();
155         Gdiplus::GdiplusShutdown( s_gdiToken );
156         ::timeEndPeriod( tc.wPeriodMin );
157         ::CoUninitialize();
158         return retVal;
159 }
160
161 static ATOM MyRegisterClass( HINSTANCE hInstance )
162 {
163         WNDCLASSEX wcex = { sizeof(wcex) };
164
165         wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC;
166         wcex.lpfnWndProc = WndProc;
167         wcex.hInstance = hInstance;
168         wcex.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE( IDR_MAINFRAME ) );
169         wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
170         wcex.hbrBackground = (HBRUSH)::GetStockObject( BLACK_BRUSH );
171         wcex.lpszMenuName       = MAKEINTRESOURCE(IDR_MAINFRAME);
172         wcex.lpszClassName = k_windowClassName;
173         wcex.hIconSm = LoadIcon( wcex.hInstance, MAKEINTRESOURCE( IDI_SMALL ) );
174
175         return RegisterClassEx( &wcex );
176 }
177
178 static BOOL InitInstance( HINSTANCE hInstance, int nCmdShow )
179 {
180         if ( !s_worldMap.loadFromFile( s_config.m_mapFileName ) ) {
181                 std::wstring fileName = s_getMapFileName();
182                 if ( !s_worldMap.loadFromFile( fileName ) ) {
183                         ::MessageBox( NULL,
184                                 L"\83}\83b\83v\89æ\91\9c\82ð\8aJ\82¯\82Ü\82¹\82ñ\82Å\82µ\82½\81B",
185                                 k_appName,
186                                 MB_ICONERROR | MB_SETFOREGROUND | MB_OK );
187                         return FALSE;
188                 }
189                 s_config.m_mapFileName = fileName;
190         }
191
192         HWND hwnd;
193
194         g_hinst = hInstance; // \83O\83\8d\81[\83o\83\8b\95Ï\90\94\82É\83C\83\93\83X\83^\83\93\83X\8f\88\97\9d\82ð\8ai\94[\82µ\82Ü\82·\81B
195
196         DWORD exStyle = 0;
197         if ( s_config.m_keepForeground ) {
198                 exStyle |= WS_EX_TOPMOST;
199         }
200         DWORD style = 0;
201         style |= WS_OVERLAPPEDWINDOW;
202         style |= WS_CLIPCHILDREN;
203         style |= WS_CLIPSIBLINGS;
204         hwnd = CreateWindowEx( exStyle, k_windowClassName, k_appName, style,
205                 s_config.m_windowPos.x, s_config.m_windowPos.y,
206                 s_config.m_windowSize.cx, s_config.m_windowSize.cy,
207                 NULL, NULL, hInstance, NULL );
208
209         if ( !hwnd ) {
210                 return FALSE;
211         }
212
213         g_hwndMain = hwnd;
214         g_hdcMain = ::GetDC( g_hwndMain );
215
216         s_renderer.setup( &s_config, g_hdcMain, &s_worldMap );
217
218         s_shipRouteList.reset( new GVOShipRouteList() );
219         s_pollingInterval = s_config.m_pollingInterval;
220         s_gvoGameProcess.setup( s_config );
221
222         s_updateWindowTitle( hwnd, s_config.m_initialSurveyCoord, s_renderer.viewScale() );
223
224         ShowWindow( hwnd, nCmdShow );
225         UpdateWindow( hwnd );
226
227         return TRUE;
228 }
229
230 static LRESULT s_mainLoop()
231 {
232         MSG msg;
233         HACCEL hAccelTable;
234
235         hAccelTable = LoadAccelerators( g_hinst, MAKEINTRESOURCE( IDR_MAINFRAME ) );
236
237         for ( ;; ) {
238                 if ( ::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
239                         if ( msg.message == WM_QUIT ) {
240                                 break;
241                         }
242                         if ( !TranslateAccelerator( msg.hwnd, hAccelTable, &msg ) ) {
243                                 TranslateMessage( &msg );
244                                 DispatchMessage( &msg );
245                         }
246                         continue;
247                 }
248                 std::vector<HANDLE> handles;
249
250                 // \8aÄ\8e\8b\82·\82é\83n\83\93\83h\83\8b\82ð\92Ç\89Á\82·\82é\81B
251                 if ( s_gvoGameProcess.processHandle() ) {
252                         handles.push_back( s_gvoGameProcess.processHandle() );
253                 }
254                 //handles.push_back( s_pollingTimerEvent );
255                 handles.push_back( s_gvoGameProcess.dataReadyEvent() );
256
257                 if ( handles.empty() ) {
258                         ::WaitMessage();
259                         continue;
260                 }
261
262                 DWORD const waitResult = ::MsgWaitForMultipleObjects( handles.size(), &handles[0], FALSE, INFINITE, QS_ALLINPUT );
263                 if ( handles.size() <= waitResult ) {
264                         continue;
265                 }
266
267                 // \8aÄ\8e\8b\82·\82é\83n\83\93\83h\83\8b\82É\91Î\89\9e\82·\82é\81B
268                 HANDLE const activeHandle = handles[waitResult];
269
270                 if ( activeHandle == s_gvoGameProcess.processHandle() ) {
271                         // \83Q\81[\83\80\83v\83\8d\83Z\83X\82ª\8fI\97¹\82µ\82½\81B
272                         s_gvoGameProcess.clear();
273                         continue;
274                 }
275
276                 if ( activeHandle == s_gvoGameProcess.dataReadyEvent() ) {
277                         s_updateFrame( g_hwndMain );
278                         continue;
279                 }
280         }
281         return (int)msg.wParam;
282 }
283
284
285 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
286 {
287         int wmId, wmEvent;
288
289         switch ( message ) {
290         case WM_ERASEBKGND:
291                 return TRUE;
292         case WM_PAINT:
293                 s_onPaint( hwnd );
294                 break;
295
296         case WM_TIMER:
297                 s_updateFrame( hwnd );
298                 break;
299
300         case WM_MOVE:
301                 s_onMove( hwnd, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
302                 break;
303         case WM_SIZE:
304                 s_onSize( hwnd, wp, LOWORD( lp ), HIWORD( lp ) );
305                 break;
306
307         case WM_COMMAND:
308                 wmId = LOWORD( wp );
309                 wmEvent = HIWORD( wp );
310                 // \91I\91ð\82³\82ê\82½\83\81\83j\83\85\81[\82Ì\89ð\90Í:
311                 switch ( wmId ) {
312                 case IDM_ABOUT:
313                         ::DialogBox( g_hinst, MAKEINTRESOURCE( IDD_ABOUTBOX ), hwnd, aboutDlgProc );
314                         break;
315                 case IDM_EXIT:
316                         DestroyWindow( hwnd );
317                         break;
318                 case IDM_TOGGLE_TRACE_SHIP:
319                         s_config.m_traceShipPositionEnabled = !s_config.m_traceShipPositionEnabled;
320                         s_renderer.enableTraceShip( s_config.m_traceShipPositionEnabled );
321                         break;
322                 case IDM_ERASE_SHIP_ROUTE:
323                         s_shipRouteList->clearAllItems();
324                         break;
325                 case IDM_TOGGLE_KEEP_FOREGROUND:
326                         s_toggleKeepForeground( hwnd );
327                         break;
328                 case IDM_TOGGLE_SPEED_METER:
329                         s_config.m_speedMeterEnabled = !s_config.m_speedMeterEnabled;
330                         s_renderer.enableSpeedMeter( s_config.m_speedMeterEnabled );
331                         ::InvalidateRect( hwnd, NULL, FALSE );
332                         break;
333                 case IDM_TOGGLE_VECTOR_LINE:
334                         s_config.m_shipVectorLineEnabled = !s_config.m_shipVectorLineEnabled;
335                         s_renderer.setVisibleShipRoute( s_config.m_shipVectorLineEnabled );
336                         break;
337                 case IDM_SAME_SCALE:
338                         if ( s_renderer.viewScale() != 1.0 ) {
339                                 s_renderer.resetViewScale();
340                         }
341                         break;
342                 case IDM_ZOOM_IN:
343                         if ( s_renderer.zoomIn() ) {
344 #ifdef GVO_PERF_CHECK
345                                 s_perfCountList.clear();
346 #endif
347                                 s_updateWindowTitle( hwnd, s_latestSurveyCoord, s_renderer.viewScale() );
348                                 ::InvalidateRect( hwnd, NULL, FALSE );
349                         }
350                         break;
351                 case IDM_ZOOM_OUT:
352                         if ( s_renderer.zoomOut() ) {
353 #ifdef GVO_PERF_CHECK
354                                 s_perfCountList.clear();
355 #endif
356                                 s_updateWindowTitle( hwnd, s_latestSurveyCoord, s_renderer.viewScale() );
357                                 ::InvalidateRect( hwnd, NULL, FALSE );
358                         }
359                         break;
360                 case IDM_SHOW_SHIPROUTEMANAGEVIEW:
361                         if ( !s_shipRouteManageView.get() ) {
362                                 s_shipRouteManageView.reset( new GVOShipRouteManageView() );
363                                 if ( !s_shipRouteManageView->setup( *s_shipRouteList.get() ) ) {
364                                         ::MessageBox( hwnd, L"\82È\82ñ\82©\82¦\82ç\81[\82Å\82·", L"\83G\83\89\81[", MB_OK | MB_ICONERROR );
365                                 }
366                         }
367                         else {
368                                 s_shipRouteManageView->activate();
369                         }
370                         break;
371 #ifndef NDEBUG
372                 case IDM_TOGGLE_DEBUG_AUTO_CRUISE:
373                         s_config.m_debugAutoCruiseEnabled = !s_config.m_debugAutoCruiseEnabled;
374                         s_gvoGameProcess.enableDebugAutoCruise( s_config.m_debugAutoCruiseEnabled );
375                         break;
376                 case IDM_DEBUG_CLOSE_ROUTE:
377                         s_closeShipRoute();
378                         break;
379                 case IDM_DEBUG_INTERVAL_NORMAL:
380                         s_pollingInterval = 1000;
381                         s_gvoGameProcess.setPollingInterval( s_pollingInterval );
382                         break;
383                 case IDM_DEBUG_INTERVAL_HIGH:
384                         s_pollingInterval = 1;
385                         s_gvoGameProcess.setPollingInterval( s_pollingInterval );
386                         break;
387 #endif
388                 default:
389                         return DefWindowProc( hwnd, message, wp, lp );
390                 }
391                 break;
392
393         case WM_MOUSEWHEEL:
394                 s_onMouseWheel( hwnd, HIWORD( wp ), LOWORD( wp ), int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
395                 break;
396         case WM_MOUSEMOVE:
397                 s_onMouseMove( hwnd, wp, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
398                 break;
399         case WM_LBUTTONDOWN:
400                 s_onMouseLeftButtonDown( hwnd, wp, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
401                 break;
402         case WM_LBUTTONUP:
403                 s_onMouseLeftButtonUp( hwnd, wp, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
404                 break;
405         case WM_RBUTTONUP:
406                 s_onMouseRightButtonUp( hwnd, wp, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
407                 break;
408         case WM_LBUTTONDBLCLK:
409                 s_onMouseLeftButtonDoubleClick( hwnd, wp, int16_t( LOWORD( lp ) ), int16_t( HIWORD( lp ) ) );
410                 break;
411
412         case WM_CREATE:
413                 if ( !s_onCreate( hwnd, reinterpret_cast<LPCREATESTRUCT>(lp) ) ) {
414                         return -1;
415                 }
416                 break;
417         case WM_DESTROY:
418                 if ( s_shipRouteManageView.get() ) {
419                         s_shipRouteManageView.reset();
420                 }
421                 s_renderer.teardown();
422                 PostQuitMessage( 0 );
423                 break;
424         default:
425                 return DefWindowProc( hwnd, message, wp, lp );
426         }
427         return 0;
428 }
429
430
431 static bool s_onCreate( HWND hwnd, LPCREATESTRUCT /*cs*/ )
432 {
433         return true;
434 }
435
436
437 static void s_onMove( HWND hwnd, WORD /*cx*/, WORD /*cy*/ )
438 {
439         const DWORD style = ::GetWindowLong( hwnd, GWL_STYLE );
440         if ( style & WS_MAXIMIZE ) {
441                 return;
442         }
443         RECT rc = { 0 };
444         ::GetWindowRect( hwnd, &rc );
445         s_config.m_windowPos.x = rc.left;
446         s_config.m_windowPos.y = rc.top;
447 }
448
449
450 static void s_onSize( HWND hwnd, UINT state, WORD cx, WORD cy )
451 {
452         RECT rc = { 0 };
453
454         switch ( state ) {
455         case SIZE_RESTORED:
456                 ::GetWindowRect( hwnd, &rc );
457                 s_config.m_windowSize.cx = rc.right - rc.left;
458                 s_config.m_windowSize.cy = rc.bottom - rc.top;
459                 break;
460         case SIZE_MAXIMIZED:
461                 break;
462         default:
463                 return;
464         }
465
466         if ( s_clientSize.cx != cx || s_clientSize.cy != cy ) {
467                 s_clientSize.cx = cx;
468                 s_clientSize.cy = cy;
469                 s_renderer.setViewSize( s_clientSize );
470         }
471 }
472
473
474 static void s_onMouseWheel( HWND hwnd, int16_t delta, UINT vkey, int16_t x, int16_t y )
475 {
476         bool isChanged = false;
477
478         if ( 0 < delta ) {
479                 isChanged = s_renderer.zoomIn();
480         }
481         else {
482                 isChanged = s_renderer.zoomOut();
483         }
484
485         if ( isChanged ) {
486 #ifdef GVO_PERF_CHECK
487                 s_perfCountList.clear();
488 #endif
489                 s_updateWindowTitle( hwnd, s_latestSurveyCoord, s_renderer.viewScale() );
490                 ::InvalidateRect( hwnd, NULL, FALSE );
491         }
492 }
493
494
495 static void s_onMouseMove( HWND hwnd, UINT vkey, int16_t x, int16_t y )
496 {
497         if ( s_isDragging ) {
498                 const int dx = x - s_dragOrg.x;
499                 const int dy = y - s_dragOrg.y;
500                 const int threshold = 1;        // \8a´\93x\82ª\97Ç\82·\82¬\82é\82Æ\92Ç\8f]\82ª\8aÈ\92P\82É\90Ø\82ê\82Ä\82µ\82Ü\82¤\82Ì\82Å\93K\93\96\82É\91Î\8dô
501                 if ( s_config.m_traceShipPositionEnabled ) {
502                         if ( ::abs( dx ) <= threshold && ::abs( dy ) < threshold ) {
503                                 return;
504                         }
505                 }
506                 const POINT offset = { -dx, -dy };
507
508                 s_renderer.offsetFocusInViewCoord( offset );
509                 ::InvalidateRect( hwnd, NULL, FALSE );
510
511                 s_dragOrg.x = x;
512                 s_dragOrg.y = y;
513                 s_config.m_traceShipPositionEnabled = false;
514                 s_renderer.enableTraceShip( s_config.m_traceShipPositionEnabled );
515         }
516         else {
517
518         }
519 }
520
521
522 static void s_onMouseLeftButtonDown( HWND hwnd, UINT vkey, int16_t x, int16_t y )
523 {
524         if ( s_isDragging ) {
525
526         }
527         else {
528                 ::SetCapture( hwnd );
529                 s_isDragging = true;
530                 s_dragOrg.x = x;
531                 s_dragOrg.y = y;
532         }
533 }
534
535
536 static void s_onMouseLeftButtonUp( HWND hwnd, UINT vkey, int16_t x, int16_t y )
537 {
538         if ( s_isDragging ) {
539                 ::ReleaseCapture();
540                 s_isDragging = false;
541                 s_dragOrg.x = 0;
542                 s_dragOrg.y = 0;
543         }
544         else {
545
546         }
547 }
548
549
550 static void s_onMouseLeftButtonDoubleClick( HWND hwnd, UINT vkey, int16_t x, int16_t y )
551 {
552         if ( s_isDragging ) {
553
554         }
555         else {
556                 s_popupCoord( hwnd, x, y );
557         }
558 }
559
560
561 static void s_onMouseRightButtonUp( HWND hwnd, UINT vkey, int16_t x, int16_t y )
562 {
563         if ( s_isDragging ) {
564
565         }
566         else {
567                 s_popupMenu( hwnd, x, y );
568         }
569 }
570
571
572 static void s_onPaint( HWND hwnd )
573 {
574 #ifdef GVO_PERF_CHECK
575         const int64_t perfBegin = g_queryPerformanceCounter();
576 #endif
577         s_renderer.render( s_latestShipVector, s_latestShipVelocity, s_shipTexture.get(), s_shipRouteList.get() );
578         ::ValidateRect( hwnd, NULL );
579
580 #ifdef GVO_PERF_CHECK
581         const int64_t perfEnd = g_queryPerformanceCounter();
582         const int64_t freq = g_queryPerformanceFrequency();
583         const double deltaPerSec = (double(perfEnd - perfBegin) / double(freq)) * 1000.0;
584         s_perfCountList.push_back(deltaPerSec);
585
586         const double ave = std::accumulate( s_perfCountList.begin(), s_perfCountList.end(), 0.0 ) / s_perfCountList.size();
587         if ( 100 < s_perfCountList.size() ) {
588                 s_perfCountList.pop_front();
589         }
590
591         std::wstring s;
592         s = std::wstring( L"\95`\89æ\91¬\93x:" ) + std::to_wstring( ave ) + L"(ms)\n";
593         ::SetWindowText( hwnd, s.c_str() );
594 #endif
595 }
596
597
598 // \83}\83b\83v\89æ\91\9c\82ð\91I\91ð\82³\82¹\82é
599 static std::wstring s_getMapFileName()
600 {
601         wchar_t dir[MAX_PATH] = { 0 };
602         ::GetModuleFileName( g_hinst, dir, _countof( dir ) );
603         ::PathRemoveFileSpec( dir );
604
605         wchar_t filePath[MAX_PATH] = { 0 };
606         OPENFILENAME ofn = { sizeof(ofn) };
607         ofn.lpstrTitle = L"\83}\83b\83v\89æ\91\9c\83t\83@\83C\83\8b\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢\81B";
608         ofn.lpstrInitialDir = &dir[0];
609         ofn.lpstrFilter = L"\83C\83\81\81[\83W\83t\83@\83C\83\8b\0L" L"*.bmp;*.jpg;*.jpeg;*.png;*.gif;*.tif;*.tiff" L"\0"
610                 L"\91S\82Ä\82Ì\83t\83@\83C\83\8b\0" L"*.*" L"\0\0";
611         ofn.Flags = OFN_READONLY | OFN_FILEMUSTEXIST;
612         ofn.nMaxFile = _countof( filePath );
613         ofn.lpstrFile = &filePath[0];
614         if ( !::GetOpenFileName( &ofn ) ) {
615                 return L"";
616         }
617         return filePath;
618 }
619
620
621 static void s_updateFrame( HWND hwnd )
622 {
623         std::vector<GVOGameStatus> gameStats;
624         gameStats = s_gvoGameProcess.getState();
625         if ( gameStats.empty() ) {
626                 return;
627         }
628
629         // \83Q\81[\83\80\83v\83\8d\83Z\83X\82Ì\83A\83C\83R\83\93\83e\83N\83X\83`\83\83\81[\82ª\96¢\8dì\90¬\82È\82ç\82Î\8dì\90¬\82ð\8e\8e\82Ý\82é
630         if ( !s_shipTexture.get() ) {
631                 const GVOImage * image = s_gvoGameProcess.shipIconImage();
632                 if ( image ) {
633                         s_shipTexture.reset( s_renderer.createTextureFromImage( *image ) );
634                 }
635         }
636
637         for ( std::vector<GVOGameStatus>::const_iterator it = gameStats.begin(); it != gameStats.end(); ++it ) {
638                 const GVOGameStatus& status = *it;
639                 s_latestSurveyCoord = status.m_surveyCoord;
640                 s_latestShipVector = status.m_shipVector;
641                 s_latestShipVelocity = status.m_shipVelocity;
642                 s_config.m_initialSurveyCoord = s_latestSurveyCoord;
643                 s_renderer.setShipPositionInWorld( s_latestSurveyCoord );
644                 if ( (s_latestTimeStamp + k_surveyCoordLostThreshold) < status.m_timeStamp ) {
645                         s_closeShipRoute();
646                 }
647                 s_latestTimeStamp = status.m_timeStamp;
648                 s_shipRouteList->addRoutePoint( s_worldMap.normalizedPoint( s_latestSurveyCoord ) );
649         }
650 #ifndef GVO_PERF_CHECK
651         s_updateWindowTitle( hwnd, s_latestSurveyCoord, s_renderer.viewScale() );
652 #endif
653         ::InvalidateRect( hwnd, NULL, FALSE );
654 }
655
656
657 static void s_updateWindowTitle( HWND hwnd, POINT surveyCoord, double viewScale )
658 {
659         std::vector<wchar_t> buf( 4096 );
660         ::swprintf( &buf[0], buf.size(), L"%d,%d - (%.1f%%) - %s %s",
661                 surveyCoord.x, surveyCoord.y,
662                 viewScale * 100.0,
663                 k_appName, k_version
664                 );
665         ::SetWindowText( hwnd, &buf[0] );
666 }
667
668
669 static void s_toggleKeepForeground( HWND hwnd )
670 {
671         if ( ::GetWindowLong( hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST ) {
672                 ::SetWindowPos( hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW );
673                 s_config.m_keepForeground = false;
674         }
675         else {
676                 ::SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW );
677                 s_config.m_keepForeground = true;
678         }
679 }
680
681
682 static void s_popupMenu( HWND hwnd, int16_t x, int16_t y )
683 {
684         HMENU hmenu = ::LoadMenu( g_hinst, MAKEINTRESOURCE( IDR_POPUPMENU ) );
685         HMENU popupMenu = ::GetSubMenu( hmenu, 0 );
686
687         ::CheckMenuItem( popupMenu, IDM_TOGGLE_TRACE_SHIP, s_config.m_traceShipPositionEnabled ? MF_CHECKED : MF_UNCHECKED );
688         ::CheckMenuItem( popupMenu, IDM_TOGGLE_KEEP_FOREGROUND, s_config.m_keepForeground ? MF_CHECKED : MF_UNCHECKED );
689         ::CheckMenuItem( popupMenu, IDM_TOGGLE_SPEED_METER, s_config.m_speedMeterEnabled ? MF_CHECKED : MF_UNCHECKED );
690         ::CheckMenuItem( popupMenu, IDM_TOGGLE_VECTOR_LINE, s_config.m_shipVectorLineEnabled ? MF_CHECKED : MF_UNCHECKED );
691
692 #ifndef NDEBUG
693         MENUITEMINFO mii = { sizeof(mii) };
694         mii.fMask = MIIM_TYPE | MIIM_ID;
695         mii.fType = MFT_STRING;
696
697         // \83f\83o\83b\83O\97p\8e©\93®\8dq\8ds\82Ì\90Ø\82è\91Ö\82¦
698         mii.wID = IDM_TOGGLE_DEBUG_AUTO_CRUISE;
699         mii.dwTypeData = L"[DEBUG]\8e©\93®\8dq\8ds\82ð\97L\8cø";
700         ::InsertMenuItem( popupMenu, ::GetMenuItemCount( popupMenu ), TRUE, &mii );
701         ::CheckMenuItem( popupMenu, IDM_TOGGLE_DEBUG_AUTO_CRUISE, s_config.m_debugAutoCruiseEnabled ? MF_CHECKED : MF_UNCHECKED );
702
703         mii.wID = IDM_DEBUG_CLOSE_ROUTE;
704         mii.dwTypeData = L"[DEBUG]\8dq\98H\82ð\95Â\82\82é";
705         ::InsertMenuItem( popupMenu, ::GetMenuItemCount( popupMenu ), TRUE, &mii );
706
707         mii.wID = IDM_DEBUG_INTERVAL_NORMAL;
708         mii.dwTypeData = L"[DEBUG]\8dX\90V\8aÔ\8au - \95W\8f\80";
709         ::InsertMenuItem( popupMenu, ::GetMenuItemCount( popupMenu ), TRUE, &mii );
710
711         mii.wID = IDM_DEBUG_INTERVAL_HIGH;
712         mii.dwTypeData = L"[DEBUG]\8dX\90V\8aÔ\8au - \8d\82\95p\93x";
713         ::InsertMenuItem( popupMenu, ::GetMenuItemCount( popupMenu ), TRUE, &mii );
714 #endif
715
716         // \83\81\83j\83\85\81[\95\\8e¦\92\86\82Í\83\81\83b\83Z\81[\83W\83|\83\93\83v\82©\82ç\94²\82¯\82ç\82ê\82È\82¢\82Ì\82Å\83^\83C\83}\81[\82Å\8dX\90V\82ð\8aÄ\8e\8b\82µ\82Ä\82¨\82­\81B
717         const UINT_PTR timerID = ::SetTimer( hwnd, 0, s_pollingInterval, NULL );
718         s_updateFrame( hwnd );
719
720         POINT p = { x, y };
721         ::ClientToScreen( hwnd, &p );
722         ::TrackPopupMenu( popupMenu, TPM_NONOTIFY | TPM_NOANIMATION | TPM_LEFTALIGN | TPM_TOPALIGN,
723                 p.x, p.y, 0, hwnd, NULL );
724         ::DestroyMenu( popupMenu );
725
726         ::KillTimer( hwnd, timerID );
727 }
728
729
730 static void s_popupCoord( HWND hwnd, int16_t x, int16_t y )
731 {
732 }
733
734
735 static void s_closeShipRoute()
736 {
737         s_shipRouteList->closeRoute();
738 }
739
740
741 BOOL CALLBACK aboutDlgProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
742 {
743         switch ( msg ) {
744         case WM_INITDIALOG:
745         {
746                 std::wstring versionString = std::wstring(k_appName) + L" " + k_version;
747                 std::wstring copyRightString = std::wstring( k_copyright );
748
749                 ::SetDlgItemText( hwnd, IDC_VERSION_LABEL, versionString.c_str() );
750                 ::SetDlgItemText( hwnd, IDC_COPYRIGHT_LABEL, copyRightString.c_str() );
751         }
752                 break;
753         case WM_COMMAND:
754                 switch ( LOWORD( wp ) ) {
755                 case IDOK:
756                 case IDCANCEL:
757                         ::EndDialog( hwnd, 0 );
758                         break;
759                 default:
760                         return FALSE;
761                 }
762                 break;
763         default:
764                 return FALSE;
765         }
766         return TRUE;
767 }