The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

897 lines
28KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace VideoRenderers
  18. {
  19. //======================================================================
  20. struct Base
  21. {
  22. virtual ~Base() {}
  23. virtual HRESULT create (ComSmartPtr<IGraphBuilder>&, ComSmartPtr<IBaseFilter>&, HWND) = 0;
  24. virtual void setVideoWindow (HWND) = 0;
  25. virtual void setVideoPosition (HWND) = 0;
  26. virtual void repaintVideo (HWND, HDC) = 0;
  27. virtual void displayModeChanged() = 0;
  28. virtual HRESULT getVideoSize (long& videoWidth, long& videoHeight) = 0;
  29. };
  30. //======================================================================
  31. struct VMR7 : public Base
  32. {
  33. VMR7() {}
  34. HRESULT create (ComSmartPtr<IGraphBuilder>& graphBuilder,
  35. ComSmartPtr<IBaseFilter>& baseFilter, HWND hwnd) override
  36. {
  37. ComSmartPtr<IVMRFilterConfig> filterConfig;
  38. HRESULT hr = baseFilter.CoCreateInstance (CLSID_VideoMixingRenderer);
  39. if (SUCCEEDED (hr)) hr = graphBuilder->AddFilter (baseFilter, L"VMR-7");
  40. if (SUCCEEDED (hr)) hr = baseFilter.QueryInterface (filterConfig);
  41. if (SUCCEEDED (hr)) hr = filterConfig->SetRenderingMode (VMRMode_Windowless);
  42. if (SUCCEEDED (hr)) hr = baseFilter.QueryInterface (windowlessControl);
  43. if (SUCCEEDED (hr)) hr = windowlessControl->SetVideoClippingWindow (hwnd);
  44. if (SUCCEEDED (hr)) hr = windowlessControl->SetAspectRatioMode (VMR_ARMODE_LETTER_BOX);
  45. return hr;
  46. }
  47. void setVideoWindow (HWND hwnd) override
  48. {
  49. windowlessControl->SetVideoClippingWindow (hwnd);
  50. }
  51. void setVideoPosition (HWND hwnd) override
  52. {
  53. long videoWidth = 0, videoHeight = 0;
  54. windowlessControl->GetNativeVideoSize (&videoWidth, &videoHeight, nullptr, nullptr);
  55. RECT src, dest;
  56. SetRect (&src, 0, 0, videoWidth, videoHeight);
  57. GetClientRect (hwnd, &dest);
  58. windowlessControl->SetVideoPosition (&src, &dest);
  59. }
  60. void repaintVideo (HWND hwnd, HDC hdc) override
  61. {
  62. windowlessControl->RepaintVideo (hwnd, hdc);
  63. }
  64. void displayModeChanged() override
  65. {
  66. windowlessControl->DisplayModeChanged();
  67. }
  68. HRESULT getVideoSize (long& videoWidth, long& videoHeight) override
  69. {
  70. return windowlessControl->GetNativeVideoSize (&videoWidth, &videoHeight, nullptr, nullptr);
  71. }
  72. ComSmartPtr<IVMRWindowlessControl> windowlessControl;
  73. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VMR7)
  74. };
  75. //======================================================================
  76. struct EVR : public Base
  77. {
  78. EVR() {}
  79. HRESULT create (ComSmartPtr<IGraphBuilder>& graphBuilder,
  80. ComSmartPtr<IBaseFilter>& baseFilter, HWND hwnd) override
  81. {
  82. ComSmartPtr<IMFGetService> getService;
  83. HRESULT hr = baseFilter.CoCreateInstance (CLSID_EnhancedVideoRenderer);
  84. if (SUCCEEDED (hr)) hr = graphBuilder->AddFilter (baseFilter, L"EVR");
  85. if (SUCCEEDED (hr)) hr = baseFilter.QueryInterface (getService);
  86. if (SUCCEEDED (hr)) hr = getService->GetService (MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl,
  87. (void**) videoDisplayControl.resetAndGetPointerAddress());
  88. if (SUCCEEDED (hr)) hr = videoDisplayControl->SetVideoWindow (hwnd);
  89. if (SUCCEEDED (hr)) hr = videoDisplayControl->SetAspectRatioMode (MFVideoARMode_PreservePicture);
  90. return hr;
  91. }
  92. void setVideoWindow (HWND hwnd) override
  93. {
  94. videoDisplayControl->SetVideoWindow (hwnd);
  95. }
  96. void setVideoPosition (HWND hwnd) override
  97. {
  98. const MFVideoNormalizedRect src = { 0.0f, 0.0f, 1.0f, 1.0f };
  99. RECT dest;
  100. GetClientRect (hwnd, &dest);
  101. videoDisplayControl->SetVideoPosition (&src, &dest);
  102. }
  103. void repaintVideo (HWND, HDC) override
  104. {
  105. videoDisplayControl->RepaintVideo();
  106. }
  107. void displayModeChanged() override {}
  108. HRESULT getVideoSize (long& videoWidth, long& videoHeight) override
  109. {
  110. SIZE sz = { 0, 0 };
  111. HRESULT hr = videoDisplayControl->GetNativeVideoSize (&sz, nullptr);
  112. videoWidth = sz.cx;
  113. videoHeight = sz.cy;
  114. return hr;
  115. }
  116. ComSmartPtr<IMFVideoDisplayControl> videoDisplayControl;
  117. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EVR)
  118. };
  119. };
  120. //==============================================================================
  121. struct VideoComponent::Pimpl : public Component
  122. {
  123. Pimpl() : videoLoaded (false)
  124. {
  125. setOpaque (true);
  126. context = new DirectShowContext (*this);
  127. componentWatcher = new ComponentWatcher (*this);
  128. }
  129. ~Pimpl()
  130. {
  131. close();
  132. context = nullptr;
  133. componentWatcher = nullptr;
  134. }
  135. Result loadFromString (const String& fileOrURLPath)
  136. {
  137. close();
  138. Result r = context->loadFile (fileOrURLPath);
  139. if (r.wasOk())
  140. {
  141. videoLoaded = true;
  142. context->updateVideoPosition();
  143. }
  144. return r;
  145. }
  146. Result load (const File& file)
  147. {
  148. Result r = loadFromString (file.getFullPathName());
  149. if (r.wasOk())
  150. currentFile = file;
  151. return r;
  152. }
  153. Result load (const URL& url)
  154. {
  155. Result r = loadFromString (url.toString (true));
  156. if (r.wasOk())
  157. currentURL = url;
  158. return r;
  159. }
  160. void close()
  161. {
  162. stop();
  163. context->release();
  164. videoLoaded = false;
  165. currentFile = File();
  166. currentURL = URL();
  167. }
  168. bool isOpen() const
  169. {
  170. return videoLoaded;
  171. }
  172. bool isPlaying() const
  173. {
  174. return context->state == DirectShowContext::runningState;
  175. }
  176. void play()
  177. {
  178. if (videoLoaded)
  179. context->play();
  180. }
  181. void stop()
  182. {
  183. if (videoLoaded)
  184. context->pause();
  185. }
  186. void setPosition (double newPosition)
  187. {
  188. if (videoLoaded)
  189. context->setPosition (newPosition);
  190. }
  191. double getPosition() const
  192. {
  193. return videoLoaded ? context->getPosition() : 0.0;
  194. }
  195. void setSpeed (double newSpeed)
  196. {
  197. if (videoLoaded)
  198. context->setSpeed (newSpeed);
  199. }
  200. Rectangle<int> getNativeSize() const
  201. {
  202. return videoLoaded ? context->getVideoSize()
  203. : Rectangle<int>();
  204. }
  205. double getDuration() const
  206. {
  207. return videoLoaded ? context->getDuration() : 0.0;
  208. }
  209. void setVolume (float newVolume)
  210. {
  211. if (videoLoaded)
  212. context->setVolume (newVolume);
  213. }
  214. float getVolume() const
  215. {
  216. return videoLoaded ? context->getVolume() : 0.0f;
  217. }
  218. void paint (Graphics& g) override
  219. {
  220. if (videoLoaded)
  221. context->handleUpdateNowIfNeeded();
  222. else
  223. g.fillAll (Colours::grey);
  224. }
  225. void updateContextPosition()
  226. {
  227. context->updateContextPosition();
  228. if (getWidth() > 0 && getHeight() > 0)
  229. if (ComponentPeer* peer = getTopLevelComponent()->getPeer())
  230. context->updateWindowPosition (peer->getAreaCoveredBy (*this));
  231. }
  232. void updateContextVisibility()
  233. {
  234. context->showWindow (isShowing());
  235. }
  236. void recreateNativeWindowAsync()
  237. {
  238. context->recreateNativeWindowAsync();
  239. repaint();
  240. }
  241. File currentFile;
  242. URL currentURL;
  243. private:
  244. bool videoLoaded;
  245. //==============================================================================
  246. struct ComponentWatcher : public ComponentMovementWatcher
  247. {
  248. ComponentWatcher (Pimpl& c) : ComponentMovementWatcher (&c), owner (c)
  249. {
  250. }
  251. void componentMovedOrResized (bool, bool) override
  252. {
  253. if (owner.videoLoaded)
  254. owner.updateContextPosition();
  255. }
  256. void componentPeerChanged() override
  257. {
  258. if (owner.videoLoaded)
  259. owner.recreateNativeWindowAsync();
  260. }
  261. void componentVisibilityChanged() override
  262. {
  263. if (owner.videoLoaded)
  264. owner.updateContextVisibility();
  265. }
  266. Pimpl& owner;
  267. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentWatcher)
  268. };
  269. ScopedPointer<ComponentWatcher> componentWatcher;
  270. //======================================================================
  271. struct DirectShowContext : public AsyncUpdater
  272. {
  273. DirectShowContext (Pimpl& c)
  274. : component (c), hwnd(), hdc(),
  275. state (uninitializedState),
  276. hasVideo (false),
  277. needToUpdateViewport (true),
  278. needToRecreateNativeWindow (false)
  279. {
  280. CoInitialize (0);
  281. }
  282. ~DirectShowContext()
  283. {
  284. release();
  285. CoUninitialize();
  286. }
  287. //======================================================================
  288. void updateWindowPosition (const Rectangle<int>& newBounds)
  289. {
  290. nativeWindow->setWindowPosition (newBounds);
  291. }
  292. void showWindow (bool shouldBeVisible)
  293. {
  294. nativeWindow->showWindow (shouldBeVisible);
  295. }
  296. //======================================================================
  297. void repaint()
  298. {
  299. if (hasVideo)
  300. videoRenderer->repaintVideo (nativeWindow->hwnd, nativeWindow->hdc);
  301. }
  302. void updateVideoPosition()
  303. {
  304. if (hasVideo)
  305. videoRenderer->setVideoPosition (nativeWindow->hwnd);
  306. }
  307. void displayResolutionChanged()
  308. {
  309. if (hasVideo)
  310. videoRenderer->displayModeChanged();
  311. }
  312. //======================================================================
  313. void peerChanged()
  314. {
  315. deleteNativeWindow();
  316. mediaEvent->SetNotifyWindow (0, 0, 0);
  317. if (videoRenderer != nullptr)
  318. videoRenderer->setVideoWindow (nullptr);
  319. createNativeWindow();
  320. mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
  321. if (videoRenderer != nullptr)
  322. videoRenderer->setVideoWindow (hwnd);
  323. }
  324. void handleAsyncUpdate() override
  325. {
  326. if (hwnd != 0)
  327. {
  328. if (needToRecreateNativeWindow)
  329. {
  330. peerChanged();
  331. needToRecreateNativeWindow = false;
  332. }
  333. if (needToUpdateViewport)
  334. {
  335. updateVideoPosition();
  336. needToUpdateViewport = false;
  337. }
  338. repaint();
  339. }
  340. else
  341. {
  342. triggerAsyncUpdate();
  343. }
  344. }
  345. void recreateNativeWindowAsync()
  346. {
  347. needToRecreateNativeWindow = true;
  348. triggerAsyncUpdate();
  349. }
  350. void updateContextPosition()
  351. {
  352. needToUpdateViewport = true;
  353. triggerAsyncUpdate();
  354. }
  355. //======================================================================
  356. Result loadFile (const String& fileOrURLPath)
  357. {
  358. jassert (state == uninitializedState);
  359. if (! createNativeWindow())
  360. return Result::fail ("Can't create window");
  361. HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph);
  362. // basic playback interfaces
  363. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaControl);
  364. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaPosition);
  365. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaEvent);
  366. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (basicAudio);
  367. // video renderer interface
  368. if (SUCCEEDED (hr))
  369. {
  370. if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista)
  371. {
  372. videoRenderer = new VideoRenderers::EVR();
  373. hr = videoRenderer->create (graphBuilder, baseFilter, hwnd);
  374. if (FAILED (hr))
  375. videoRenderer = nullptr;
  376. }
  377. if (videoRenderer == nullptr)
  378. {
  379. videoRenderer = new VideoRenderers::VMR7();
  380. hr = videoRenderer->create (graphBuilder, baseFilter, hwnd);
  381. }
  382. }
  383. // build filter graph
  384. if (SUCCEEDED (hr))
  385. {
  386. hr = graphBuilder->RenderFile (fileOrURLPath.toWideCharPointer(), nullptr);
  387. if (FAILED (hr))
  388. {
  389. // Annoyingly, if we don't run the msg loop between failing and deleting the window, the
  390. // whole OS message-dispatch system gets itself into a state, and refuses to deliver any
  391. // more messages for the whole app. (That's what happens in Win7, anyway)
  392. MessageManager::getInstance()->runDispatchLoopUntil (200);
  393. }
  394. }
  395. // remove video renderer if not connected (no video)
  396. if (SUCCEEDED (hr))
  397. {
  398. if (isRendererConnected())
  399. {
  400. hasVideo = true;
  401. }
  402. else
  403. {
  404. hasVideo = false;
  405. graphBuilder->RemoveFilter (baseFilter);
  406. videoRenderer = nullptr;
  407. baseFilter = nullptr;
  408. }
  409. }
  410. // set window to receive events
  411. if (SUCCEEDED (hr))
  412. hr = mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
  413. if (SUCCEEDED (hr))
  414. {
  415. state = stoppedState;
  416. pause();
  417. return Result::ok();
  418. }
  419. // Note that if you're trying to open a file and this method fails, you may
  420. // just need to install a suitable codec. It seems that by default DirectShow
  421. // doesn't support a very good range of formats.
  422. release();
  423. return getErrorMessageFromResult (hr);
  424. }
  425. static Result getErrorMessageFromResult (HRESULT hr)
  426. {
  427. switch (hr)
  428. {
  429. case VFW_E_INVALID_FILE_FORMAT: return Result::fail ("Invalid file format");
  430. case VFW_E_NOT_FOUND: return Result::fail ("File not found");
  431. case VFW_E_UNKNOWN_FILE_TYPE: return Result::fail ("Unknown file type");
  432. case VFW_E_UNSUPPORTED_STREAM: return Result::fail ("Unsupported stream");
  433. case VFW_E_CANNOT_CONNECT: return Result::fail ("Cannot connect");
  434. case VFW_E_CANNOT_LOAD_SOURCE_FILTER: return Result::fail ("Cannot load source filter");
  435. }
  436. TCHAR messageBuffer[512] = { 0 };
  437. FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  438. nullptr, hr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
  439. messageBuffer, (DWORD) numElementsInArray (messageBuffer) - 1, nullptr);
  440. return Result::fail (String (messageBuffer));
  441. }
  442. void release()
  443. {
  444. if (mediaControl != nullptr)
  445. mediaControl->Stop();
  446. if (mediaEvent != nullptr)
  447. mediaEvent->SetNotifyWindow (0, 0, 0);
  448. if (videoRenderer != nullptr)
  449. videoRenderer->setVideoWindow (0);
  450. hasVideo = false;
  451. videoRenderer = nullptr;
  452. baseFilter = nullptr;
  453. basicAudio = nullptr;
  454. mediaEvent = nullptr;
  455. mediaPosition = nullptr;
  456. mediaControl = nullptr;
  457. graphBuilder = nullptr;
  458. state = uninitializedState;
  459. if (nativeWindow != nullptr)
  460. deleteNativeWindow();
  461. }
  462. void graphEventProc()
  463. {
  464. LONG ec = 0;
  465. LONG_PTR p1 = {}, p2 = {};
  466. jassert (mediaEvent != nullptr);
  467. while (SUCCEEDED (mediaEvent->GetEvent (&ec, &p1, &p2, 0)))
  468. {
  469. mediaEvent->FreeEventParams (ec, p1, p2);
  470. switch (ec)
  471. {
  472. case EC_REPAINT:
  473. component.repaint();
  474. break;
  475. case EC_COMPLETE:
  476. component.stop();
  477. break;
  478. case EC_USERABORT:
  479. case EC_ERRORABORT:
  480. case EC_ERRORABORTEX:
  481. component.close();
  482. break;
  483. default:
  484. break;
  485. }
  486. }
  487. }
  488. //======================================================================
  489. void play()
  490. {
  491. mediaControl->Run();
  492. state = runningState;
  493. }
  494. void stop()
  495. {
  496. mediaControl->Stop();
  497. state = stoppedState;
  498. }
  499. void pause()
  500. {
  501. mediaControl->Pause();
  502. state = pausedState;
  503. }
  504. //======================================================================
  505. Rectangle<int> getVideoSize() const noexcept
  506. {
  507. long width = 0, height = 0;
  508. if (hasVideo)
  509. videoRenderer->getVideoSize (width, height);
  510. return Rectangle<int> ((int) width, (int) height);
  511. }
  512. //======================================================================
  513. double getDuration() const
  514. {
  515. REFTIME duration;
  516. mediaPosition->get_Duration (&duration);
  517. return duration;
  518. }
  519. double getPosition() const
  520. {
  521. REFTIME seconds;
  522. mediaPosition->get_CurrentPosition (&seconds);
  523. return seconds;
  524. }
  525. void setSpeed (double newSpeed) { mediaPosition->put_Rate (newSpeed); }
  526. void setPosition (double seconds) { mediaPosition->put_CurrentPosition (seconds); }
  527. void setVolume (float newVolume) { basicAudio->put_Volume (convertToDShowVolume (newVolume)); }
  528. // in DirectShow, full volume is 0, silence is -10000
  529. static long convertToDShowVolume (float vol) noexcept
  530. {
  531. if (vol >= 1.0f) return 0;
  532. if (vol <= 0.0f) return -10000;
  533. return roundToInt ((vol * 10000.0f) - 10000.0f);
  534. }
  535. float getVolume() const
  536. {
  537. long volume;
  538. basicAudio->get_Volume (&volume);
  539. return (volume + 10000) / 10000.0f;
  540. }
  541. enum State { uninitializedState, runningState, pausedState, stoppedState };
  542. State state;
  543. private:
  544. //======================================================================
  545. enum { graphEventID = WM_APP + 0x43f0 };
  546. Pimpl& component;
  547. HWND hwnd;
  548. HDC hdc;
  549. ComSmartPtr<IGraphBuilder> graphBuilder;
  550. ComSmartPtr<IMediaControl> mediaControl;
  551. ComSmartPtr<IMediaPosition> mediaPosition;
  552. ComSmartPtr<IMediaEventEx> mediaEvent;
  553. ComSmartPtr<IBasicAudio> basicAudio;
  554. ComSmartPtr<IBaseFilter> baseFilter;
  555. ScopedPointer<VideoRenderers::Base> videoRenderer;
  556. bool hasVideo, needToUpdateViewport, needToRecreateNativeWindow;
  557. //======================================================================
  558. bool createNativeWindow()
  559. {
  560. jassert (nativeWindow == nullptr);
  561. if (ComponentPeer* const topLevelPeer = component.getTopLevelComponent()->getPeer())
  562. {
  563. nativeWindow = new NativeWindow ((HWND) topLevelPeer->getNativeHandle(), this);
  564. hwnd = nativeWindow->hwnd;
  565. if (hwnd != 0)
  566. {
  567. hdc = GetDC (hwnd);
  568. component.updateContextPosition();
  569. component.updateContextVisibility();
  570. return true;
  571. }
  572. nativeWindow = nullptr;
  573. }
  574. else
  575. {
  576. jassertfalse;
  577. }
  578. return false;
  579. }
  580. void deleteNativeWindow()
  581. {
  582. jassert (nativeWindow != nullptr);
  583. ReleaseDC (hwnd, hdc);
  584. hwnd = 0;
  585. hdc = 0;
  586. nativeWindow = nullptr;
  587. }
  588. bool isRendererConnected()
  589. {
  590. ComSmartPtr<IEnumPins> enumPins;
  591. HRESULT hr = baseFilter->EnumPins (enumPins.resetAndGetPointerAddress());
  592. if (SUCCEEDED (hr))
  593. hr = enumPins->Reset();
  594. ComSmartPtr<IPin> pin;
  595. while (SUCCEEDED (hr)
  596. && enumPins->Next (1, pin.resetAndGetPointerAddress(), nullptr) == S_OK)
  597. {
  598. ComSmartPtr<IPin> otherPin;
  599. hr = pin->ConnectedTo (otherPin.resetAndGetPointerAddress());
  600. if (SUCCEEDED (hr))
  601. {
  602. PIN_DIRECTION direction;
  603. hr = pin->QueryDirection (&direction);
  604. if (SUCCEEDED (hr) && direction == PINDIR_INPUT)
  605. return true;
  606. }
  607. else if (hr == VFW_E_NOT_CONNECTED)
  608. {
  609. hr = S_OK;
  610. }
  611. }
  612. return false;
  613. }
  614. //======================================================================
  615. struct NativeWindowClass : private DeletedAtShutdown
  616. {
  617. bool isRegistered() const noexcept { return atom != 0; }
  618. LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) MAKELONG (atom, 0); }
  619. juce_DeclareSingleton_SingleThreaded_Minimal (NativeWindowClass)
  620. private:
  621. NativeWindowClass() : atom()
  622. {
  623. String windowClassName ("JUCE_DIRECTSHOW_");
  624. windowClassName << (int) (Time::currentTimeMillis() & 0x7fffffff);
  625. HINSTANCE moduleHandle = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
  626. TCHAR moduleFile [1024] = { 0 };
  627. GetModuleFileName (moduleHandle, moduleFile, 1024);
  628. WNDCLASSEX wcex = { 0 };
  629. wcex.cbSize = sizeof (wcex);
  630. wcex.style = CS_OWNDC;
  631. wcex.lpfnWndProc = (WNDPROC) wndProc;
  632. wcex.lpszClassName = windowClassName.toWideCharPointer();
  633. wcex.hInstance = moduleHandle;
  634. atom = RegisterClassEx (&wcex);
  635. jassert (atom != 0);
  636. }
  637. ~NativeWindowClass()
  638. {
  639. if (atom != 0)
  640. UnregisterClass (getWindowClassName(), (HINSTANCE) Process::getCurrentModuleInstanceHandle());
  641. clearSingletonInstance();
  642. }
  643. static LRESULT CALLBACK wndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  644. {
  645. if (DirectShowContext* const c
  646. = (DirectShowContext*) GetWindowLongPtr (hwnd, GWLP_USERDATA))
  647. {
  648. switch (msg)
  649. {
  650. case WM_NCHITTEST: return HTTRANSPARENT;
  651. case WM_ERASEBKGND: return 1;
  652. case WM_DISPLAYCHANGE: c->displayResolutionChanged(); break;
  653. case graphEventID: c->graphEventProc(); return 0;
  654. default: break;
  655. }
  656. }
  657. return DefWindowProc (hwnd, msg, wParam, lParam);
  658. }
  659. ATOM atom;
  660. JUCE_DECLARE_NON_COPYABLE (NativeWindowClass)
  661. };
  662. //======================================================================
  663. struct NativeWindow
  664. {
  665. NativeWindow (HWND parentToAddTo, void* userData) : hwnd(), hdc()
  666. {
  667. NativeWindowClass* wc = NativeWindowClass::getInstance();
  668. if (wc->isRegistered())
  669. {
  670. DWORD exstyle = 0;
  671. DWORD type = WS_CHILD;
  672. hwnd = CreateWindowEx (exstyle, wc->getWindowClassName(),
  673. L"", type, 0, 0, 0, 0, parentToAddTo, 0,
  674. (HINSTANCE) Process::getCurrentModuleInstanceHandle(), 0);
  675. if (hwnd != 0)
  676. {
  677. hdc = GetDC (hwnd);
  678. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) userData);
  679. }
  680. }
  681. jassert (hwnd != 0);
  682. }
  683. ~NativeWindow()
  684. {
  685. if (hwnd != 0)
  686. {
  687. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) 0);
  688. DestroyWindow (hwnd);
  689. }
  690. }
  691. void setWindowPosition (Rectangle<int> newBounds)
  692. {
  693. SetWindowPos (hwnd, 0, newBounds.getX(), newBounds.getY(),
  694. newBounds.getWidth(), newBounds.getHeight(),
  695. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  696. }
  697. void showWindow (const bool shouldBeVisible)
  698. {
  699. ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
  700. }
  701. HWND hwnd;
  702. HDC hdc;
  703. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeWindow)
  704. };
  705. ScopedPointer<NativeWindow> nativeWindow;
  706. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowContext)
  707. };
  708. ScopedPointer<DirectShowContext> context;
  709. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  710. };
  711. juce_ImplementSingleton_SingleThreaded (VideoComponent::Pimpl::DirectShowContext::NativeWindowClass)