WebKit/win/FullscreenVideoController.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2010 Apple Inc. All rights reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  * 1. Redistributions of source code must retain the above copyright
       
     8  *    notice, this list of conditions and the following disclaimer.
       
     9  * 2. Redistributions in binary form must reproduce the above copyright
       
    10  *    notice, this list of conditions and the following disclaimer in the
       
    11  *    documentation and/or other materials provided with the distribution.
       
    12  *
       
    13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
       
    14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
       
    17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
       
    21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
       
    24  */
       
    25 
       
    26 #include "config.h"
       
    27 
       
    28 #if ENABLE(VIDEO)
       
    29 
       
    30 #include "FullscreenVideoController.h"
       
    31 
       
    32 #include "WebKitDLL.h"
       
    33 #include "WebView.h"
       
    34 #include <ApplicationServices/ApplicationServices.h>
       
    35 #include <WebCore/BitmapInfo.h>
       
    36 #include <WebCore/Chrome.h>
       
    37 #include <WebCore/Font.h>
       
    38 #include <WebCore/FontSelector.h>
       
    39 #include <WebCore/GraphicsContext.h>
       
    40 #include <WebCore/Page.h>
       
    41 #include <WebCore/TextRun.h>
       
    42 #include <WebCore/WKCACFLayer.h>
       
    43 #include <WebKitSystemInterface/WebKitSystemInterface.h>
       
    44 #include <windowsx.h>
       
    45 #include <wtf/StdLibExtras.h>
       
    46 
       
    47 using namespace std;
       
    48 using namespace WebCore;
       
    49 
       
    50 static const float timerInterval = 0.033;
       
    51 
       
    52 // HUD Size
       
    53 static const int windowHeight = 59;
       
    54 static const int windowWidth = 438;
       
    55 
       
    56 // Margins and button sizes
       
    57 static const int margin = 9;
       
    58 static const int marginTop = 9;
       
    59 static const int buttonSize = 25;
       
    60 static const int buttonMiniSize = 16;
       
    61 static const int volumeSliderWidth = 50;
       
    62 static const int timeSliderWidth = 310;
       
    63 static const int sliderHeight = 8;
       
    64 static const int volumeSliderButtonSize = 10;
       
    65 static const int timeSliderButtonSize = 8;
       
    66 static const int textSize = 11;
       
    67 static const float initialHUDPositionY = 0.9; // Initial Y position of HUD in percentage from top of screen
       
    68 
       
    69 // Background values
       
    70 static const int borderRadius = 12;
       
    71 static const int borderThickness = 2;
       
    72 
       
    73 // Colors
       
    74 static const unsigned int backgroundColor = 0xA0202020;
       
    75 static const unsigned int borderColor = 0xFFA0A0A0;
       
    76 static const unsigned int sliderGutterColor = 0xFF141414;
       
    77 static const unsigned int sliderButtonColor = 0xFF808080;
       
    78 static const unsigned int textColor = 0xFFFFFFFF;
       
    79 
       
    80 HUDButton::HUDButton(HUDButtonType type, const IntPoint& position)
       
    81     : HUDWidget(IntRect(position, IntSize()))
       
    82     , m_type(type)
       
    83     , m_showAltButton(false)
       
    84 {
       
    85     const char* buttonResource = 0;
       
    86     const char* buttonResourceAlt = 0;
       
    87     switch (m_type) {
       
    88     case PlayPauseButton:
       
    89         buttonResource = "fsVideoPlay";
       
    90         buttonResourceAlt = "fsVideoPause";
       
    91         break;
       
    92     case TimeSliderButton:
       
    93         break;
       
    94     case VolumeUpButton:
       
    95         buttonResource = "fsVideoAudioVolumeHigh";
       
    96         break;
       
    97     case VolumeSliderButton:
       
    98         break;
       
    99     case VolumeDownButton:
       
   100         buttonResource = "fsVideoAudioVolumeLow";
       
   101         break;
       
   102     case ExitFullscreenButton:
       
   103         buttonResource = "fsVideoExitFullscreen";
       
   104         break;
       
   105     }
       
   106 
       
   107     if (buttonResource) {
       
   108         m_buttonImage = Image::loadPlatformResource(buttonResource);
       
   109         m_rect.setWidth(m_buttonImage->width());
       
   110         m_rect.setHeight(m_buttonImage->height());
       
   111     }
       
   112     if (buttonResourceAlt)
       
   113         m_buttonImageAlt = Image::loadPlatformResource(buttonResourceAlt);
       
   114 }
       
   115 
       
   116 void HUDButton::draw(GraphicsContext& context)
       
   117 {
       
   118     Image* image = (m_showAltButton && m_buttonImageAlt) ? m_buttonImageAlt.get() : m_buttonImage.get();
       
   119     context.drawImage(image, DeviceColorSpace, m_rect.location());
       
   120 }
       
   121 
       
   122 HUDSlider::HUDSlider(HUDSliderButtonShape shape, int buttonSize, const IntRect& rect)
       
   123     : HUDWidget(rect)
       
   124     , m_buttonShape(shape)
       
   125     , m_buttonSize(buttonSize)
       
   126     , m_buttonPosition(0)
       
   127     , m_dragStartOffset(0)
       
   128 {
       
   129 }
       
   130 
       
   131 void HUDSlider::draw(GraphicsContext& context)
       
   132 {
       
   133     // Draw gutter
       
   134     IntSize radius(m_rect.height() / 2, m_rect.height() / 2);
       
   135     context.fillRoundedRect(m_rect, radius, radius, radius, radius, Color(sliderGutterColor), DeviceColorSpace);
       
   136 
       
   137     // Draw button
       
   138     context.setStrokeColor(Color(sliderButtonColor), DeviceColorSpace);
       
   139     context.setFillColor(Color(sliderButtonColor), DeviceColorSpace);
       
   140 
       
   141     if (m_buttonShape == RoundButton) {
       
   142         context.drawEllipse(IntRect(m_rect.location().x() + m_buttonPosition, m_rect.location().y() - (m_buttonSize - m_rect.height()) / 2, m_buttonSize, m_buttonSize));
       
   143         return;
       
   144     }
       
   145 
       
   146     // Draw a diamond
       
   147     FloatPoint points[4];
       
   148     float half = static_cast<float>(m_buttonSize) / 2;
       
   149     points[0].setX(m_rect.location().x() + m_buttonPosition + half);
       
   150     points[0].setY(m_rect.location().y());
       
   151     points[1].setX(m_rect.location().x() + m_buttonPosition + m_buttonSize);
       
   152     points[1].setY(m_rect.location().y() + half);
       
   153     points[2].setX(m_rect.location().x() + m_buttonPosition + half);
       
   154     points[2].setY(m_rect.location().y() + m_buttonSize);
       
   155     points[3].setX(m_rect.location().x() + m_buttonPosition);
       
   156     points[3].setY(m_rect.location().y() + half);
       
   157     context.drawConvexPolygon(4, points, true);
       
   158 }
       
   159 
       
   160 void HUDSlider::drag(const IntPoint& point, bool start)
       
   161 {
       
   162     if (start) {
       
   163         // When we start, we need to snap the slider position to the x position if we clicked the gutter.
       
   164         // But if we click the button, we need to drag relative to where we clicked down. We only need
       
   165         // to check X because we would not even get here unless Y were already inside.
       
   166         int relativeX = point.x() - m_rect.location().x();
       
   167         if (relativeX >= m_buttonPosition && relativeX <= m_buttonPosition + m_buttonSize)
       
   168             m_dragStartOffset = point.x() - m_buttonPosition;
       
   169         else
       
   170             m_dragStartOffset = m_rect.location().x() + m_buttonSize / 2;
       
   171     }
       
   172 
       
   173     m_buttonPosition = max(0, min(m_rect.width() - m_buttonSize, point.x() - m_dragStartOffset));
       
   174 }
       
   175 
       
   176 #if USE(ACCELERATED_COMPOSITING)
       
   177 class FullscreenVideoController::LayoutClient : public WKCACFLayerLayoutClient {
       
   178 public:
       
   179     LayoutClient(FullscreenVideoController* parent);
       
   180     void layoutSublayersOfLayer(WKCACFLayer* layer);
       
   181 
       
   182     FullscreenVideoController* m_parent;
       
   183 };
       
   184 
       
   185 FullscreenVideoController::LayoutClient::LayoutClient(FullscreenVideoController* parent)
       
   186     : m_parent(parent)
       
   187 {
       
   188 }
       
   189 
       
   190 void FullscreenVideoController::LayoutClient::layoutSublayersOfLayer(WKCACFLayer* layer) 
       
   191 {
       
   192     ASSERT_ARG(layer, layer == m_parent->m_rootChild);
       
   193 
       
   194     HTMLMediaElement* mediaElement = m_parent->m_mediaElement.get();
       
   195     if (!mediaElement)
       
   196         return;
       
   197 
       
   198     WKCACFLayer* videoLayer = mediaElement->platformLayer();
       
   199     if (!videoLayer || videoLayer->superlayer() != layer)
       
   200         return;
       
   201 
       
   202     FloatRect layerBounds = layer->bounds();
       
   203 
       
   204     FloatSize videoSize = mediaElement->player()->naturalSize();
       
   205     float scaleFactor;
       
   206     if (videoSize.aspectRatio() > layerBounds.size().aspectRatio())
       
   207         scaleFactor = layerBounds.width() / videoSize.width();
       
   208     else
       
   209         scaleFactor = layerBounds.height() / videoSize.height();
       
   210     videoSize.scale(scaleFactor);
       
   211 
       
   212     // Calculate the centered position based on the videoBounds and layerBounds:
       
   213     FloatPoint videoPosition;
       
   214     FloatPoint videoOrigin;
       
   215     videoOrigin.setX((layerBounds.width() - videoSize.width()) * 0.5);
       
   216     videoOrigin.setY((layerBounds.height() - videoSize.height()) * 0.5);
       
   217     videoLayer->setFrame(FloatRect(videoOrigin, videoSize));
       
   218 }
       
   219 #endif 
       
   220 
       
   221 FullscreenVideoController::FullscreenVideoController()
       
   222     : m_hudWindow(0)
       
   223     , m_playPauseButton(HUDButton::PlayPauseButton, IntPoint((windowWidth - buttonSize) / 2, marginTop))
       
   224     , m_timeSliderButton(HUDButton::TimeSliderButton, IntPoint(0, 0))
       
   225     , m_volumeUpButton(HUDButton::VolumeUpButton, IntPoint(margin + buttonMiniSize + volumeSliderWidth + buttonMiniSize / 2, marginTop + (buttonSize - buttonMiniSize) / 2))
       
   226     , m_volumeSliderButton(HUDButton::VolumeSliderButton, IntPoint(0, 0))
       
   227     , m_volumeDownButton(HUDButton::VolumeDownButton, IntPoint(margin, marginTop + (buttonSize - buttonMiniSize) / 2))
       
   228     , m_exitFullscreenButton(HUDButton::ExitFullscreenButton, IntPoint(windowWidth - 2 * margin - buttonMiniSize, marginTop + (buttonSize - buttonMiniSize) / 2))
       
   229     , m_volumeSlider(HUDSlider::RoundButton, volumeSliderButtonSize, IntRect(IntPoint(margin + buttonMiniSize, marginTop + (buttonSize - buttonMiniSize) / 2 + buttonMiniSize / 2 - sliderHeight / 2), IntSize(volumeSliderWidth, sliderHeight)))
       
   230     , m_timeSlider(HUDSlider::DiamondButton, timeSliderButtonSize, IntRect(IntPoint(windowWidth / 2 - timeSliderWidth / 2, windowHeight - margin - sliderHeight), IntSize(timeSliderWidth, sliderHeight)))
       
   231     , m_hitWidget(0)
       
   232     , m_movingWindow(false)
       
   233     , m_timer(this, &FullscreenVideoController::timerFired)
       
   234 #if USE(ACCELERATED_COMPOSITING)
       
   235     , m_rootChild(WKCACFLayer::create(WKCACFLayer::Layer))
       
   236     , m_layoutClient(new LayoutClient(this))
       
   237 #endif
       
   238     , m_fullscreenWindow(new MediaPlayerPrivateFullscreenWindow(this))
       
   239 {
       
   240 #if USE(ACCELERATED_COMPOSITING)
       
   241     m_rootChild->setLayoutClient(m_layoutClient.get());
       
   242 #endif
       
   243 }
       
   244 
       
   245 FullscreenVideoController::~FullscreenVideoController()
       
   246 {
       
   247 #if USE(ACCELERATED_COMPOSITING)
       
   248     m_rootChild->setLayoutClient(0);
       
   249 #endif
       
   250 }
       
   251 
       
   252 void FullscreenVideoController::setMediaElement(HTMLMediaElement* mediaElement)
       
   253 {
       
   254     if (mediaElement == m_mediaElement)
       
   255         return;
       
   256 
       
   257     m_mediaElement = mediaElement;
       
   258     if (!m_mediaElement) {
       
   259         // Can't do full-screen, just get out
       
   260         exitFullscreen();
       
   261     }
       
   262 }
       
   263 
       
   264 void FullscreenVideoController::enterFullscreen()
       
   265 {
       
   266     if (!m_mediaElement)
       
   267         return;
       
   268 
       
   269     WebView* webView = kit(m_mediaElement->document()->page());
       
   270     HWND parentHwnd = webView ? webView->viewWindow() : 0;
       
   271 
       
   272     m_fullscreenWindow->createWindow(parentHwnd);
       
   273 #if USE(ACCELERATED_COMPOSITING)
       
   274     m_fullscreenWindow->setRootChildLayer(m_rootChild);
       
   275 
       
   276     WKCACFLayer* videoLayer = m_mediaElement->player()->platformLayer();
       
   277     m_rootChild->addSublayer(videoLayer);
       
   278     m_rootChild->setNeedsLayout();
       
   279 #endif
       
   280 
       
   281     RECT windowRect;
       
   282     GetClientRect(m_fullscreenWindow->hwnd(), &windowRect);
       
   283     m_fullscreenSize.setWidth(windowRect.right - windowRect.left);
       
   284     m_fullscreenSize.setHeight(windowRect.bottom - windowRect.top);
       
   285 
       
   286     createHUDWindow();
       
   287 }
       
   288 
       
   289 void FullscreenVideoController::exitFullscreen()
       
   290 {
       
   291     SetWindowLongPtr(m_hudWindow, 0, 0);
       
   292 
       
   293     if (m_fullscreenWindow)
       
   294         m_fullscreenWindow = 0;
       
   295 
       
   296     ASSERT(!IsWindow(m_hudWindow));
       
   297     m_hudWindow = 0;
       
   298 
       
   299     // We previously ripped the mediaElement's platform layer out
       
   300     // of its orginial layer tree to display it in our fullscreen
       
   301     // window.  Now, we need to get the layer back in its original
       
   302     // tree.
       
   303     // 
       
   304     // As a side effect of setting the player to invisible/visible,
       
   305     // the player's layer will be recreated, and will be picked up 
       
   306     // the next time the layer tree is synched.
       
   307     m_mediaElement->player()->setVisible(0);
       
   308     m_mediaElement->player()->setVisible(1);
       
   309 }
       
   310 
       
   311 bool FullscreenVideoController::canPlay() const
       
   312 {
       
   313     return m_mediaElement && m_mediaElement->canPlay();
       
   314 }
       
   315 
       
   316 void FullscreenVideoController::play()
       
   317 {
       
   318     if (m_mediaElement)
       
   319         m_mediaElement->play(m_mediaElement->processingUserGesture());
       
   320 }
       
   321 
       
   322 void FullscreenVideoController::pause()
       
   323 {
       
   324     if (m_mediaElement)
       
   325         m_mediaElement->pause(m_mediaElement->processingUserGesture());
       
   326 }
       
   327 
       
   328 float FullscreenVideoController::volume() const
       
   329 {
       
   330     return m_mediaElement ? m_mediaElement->volume() : 0;
       
   331 }
       
   332 
       
   333 void FullscreenVideoController::setVolume(float volume)
       
   334 {
       
   335     if (m_mediaElement) {
       
   336         ExceptionCode ec;
       
   337         m_mediaElement->setVolume(volume, ec);
       
   338     }
       
   339 }
       
   340 
       
   341 float FullscreenVideoController::currentTime() const
       
   342 {
       
   343     return m_mediaElement ? m_mediaElement->currentTime() : 0;
       
   344 }
       
   345 
       
   346 void FullscreenVideoController::setCurrentTime(float value)
       
   347 {
       
   348     if (m_mediaElement) {
       
   349         ExceptionCode ec;
       
   350         m_mediaElement->setCurrentTime(value, ec);
       
   351     }
       
   352 }
       
   353 
       
   354 float FullscreenVideoController::duration() const
       
   355 {
       
   356     return m_mediaElement ? m_mediaElement->duration() : 0;
       
   357 }
       
   358 
       
   359 void FullscreenVideoController::beginScrubbing()
       
   360 {
       
   361     if (m_mediaElement) 
       
   362         m_mediaElement->beginScrubbing();
       
   363 }
       
   364 
       
   365 void FullscreenVideoController::endScrubbing()
       
   366 {
       
   367     if (m_mediaElement) 
       
   368         m_mediaElement->endScrubbing();
       
   369 }
       
   370 
       
   371 LRESULT FullscreenVideoController::fullscreenClientWndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam)
       
   372 {
       
   373     switch (message) {
       
   374     case WM_CHAR:
       
   375         onChar(wParam);
       
   376         break;
       
   377     case WM_KEYDOWN:
       
   378         onKeyDown(wParam);
       
   379         break;
       
   380     case WM_LBUTTONDOWN:
       
   381         onMouseDown(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
       
   382         break;
       
   383     case WM_MOUSEMOVE:
       
   384         onMouseMove(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
       
   385         break;
       
   386     case WM_LBUTTONUP:
       
   387         onMouseUp(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
       
   388         break;
       
   389     }
       
   390 
       
   391     return DefWindowProc(wnd, message, wParam, lParam);
       
   392 }
       
   393 
       
   394 static const LPCWSTR fullscreenVideeoHUDWindowClassName = L"fullscreenVideeoHUDWindowClass";
       
   395 
       
   396 void FullscreenVideoController::registerHUDWindowClass()
       
   397 {
       
   398     static bool haveRegisteredHUDWindowClass;
       
   399     if (haveRegisteredHUDWindowClass)
       
   400         return;
       
   401 
       
   402     haveRegisteredHUDWindowClass = true;
       
   403 
       
   404     WNDCLASSEX wcex;
       
   405 
       
   406     wcex.cbSize = sizeof(WNDCLASSEX);
       
   407 
       
   408     wcex.style = CS_HREDRAW | CS_VREDRAW;
       
   409     wcex.lpfnWndProc = hudWndProc;
       
   410     wcex.cbClsExtra = 0;
       
   411     wcex.cbWndExtra = 4;
       
   412     wcex.hInstance = gInstance;
       
   413     wcex.hIcon = 0;
       
   414     wcex.hCursor = LoadCursor(0, IDC_ARROW);
       
   415     wcex.hbrBackground = 0;
       
   416     wcex.lpszMenuName = 0;
       
   417     wcex.lpszClassName = fullscreenVideeoHUDWindowClassName;
       
   418     wcex.hIconSm = 0;
       
   419 
       
   420     RegisterClassEx(&wcex);
       
   421 }
       
   422 
       
   423 void FullscreenVideoController::createHUDWindow()
       
   424 {
       
   425     m_hudPosition.setX((m_fullscreenSize.width() - windowWidth) / 2);
       
   426     m_hudPosition.setY(m_fullscreenSize.height() * initialHUDPositionY - windowHeight / 2);
       
   427 
       
   428     // Local variable that will hold the returned pixels. No need to cleanup this value. It
       
   429     // will get cleaned up when m_bitmap is destroyed in the dtor
       
   430     void* pixels;
       
   431     BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(IntSize(windowWidth, windowHeight));
       
   432     m_bitmap.set(::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0));
       
   433     
       
   434     // Dirty the window so the HUD draws
       
   435     RECT clearRect = { m_hudPosition.x(), m_hudPosition.y(), m_hudPosition.x() + windowWidth, m_hudPosition.y() + windowHeight };
       
   436     InvalidateRect(m_fullscreenWindow->hwnd(), &clearRect, true);
       
   437 
       
   438     m_playPauseButton.setShowAltButton(!canPlay());
       
   439     m_volumeSlider.setValue(volume());
       
   440     m_timeSlider.setValue(currentTime() / duration());
       
   441 
       
   442     if (!canPlay())
       
   443         m_timer.startRepeating(timerInterval);
       
   444 
       
   445     registerHUDWindowClass();
       
   446 
       
   447     m_hudWindow = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, 
       
   448         fullscreenVideeoHUDWindowClassName, 0, WS_POPUP | WS_VISIBLE,
       
   449         m_hudPosition.x(), m_hudPosition.y(), 0, 0, m_fullscreenWindow->hwnd(), 0, gInstance, 0);
       
   450     ASSERT(::IsWindow(m_hudWindow));
       
   451     SetWindowLongPtr(m_hudWindow, 0, reinterpret_cast<LONG_PTR>(this));
       
   452 
       
   453     draw();
       
   454 }
       
   455 
       
   456 static String timeToString(float time)
       
   457 {
       
   458     if (!isfinite(time))
       
   459         time = 0;
       
   460     int seconds = fabsf(time); 
       
   461     int hours = seconds / (60 * 60);
       
   462     int minutes = (seconds / 60) % 60;
       
   463     seconds %= 60;
       
   464 
       
   465     if (hours) {
       
   466         if (hours > 9)
       
   467             return String::format("%s%02d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds);
       
   468         return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds);
       
   469     }
       
   470 
       
   471     return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, seconds);
       
   472 }
       
   473 
       
   474 void FullscreenVideoController::draw()
       
   475 {
       
   476     HDC windowDC = GetDC(m_hudWindow);
       
   477     HDC bitmapDC = CreateCompatibleDC(windowDC);
       
   478     ::ReleaseDC(m_hudWindow, windowDC);
       
   479     SelectObject(bitmapDC, m_bitmap.get());
       
   480 
       
   481     GraphicsContext context(bitmapDC, true);
       
   482 
       
   483     context.save();
       
   484 
       
   485     // Draw the background
       
   486     IntSize outerRadius(borderRadius, borderRadius);
       
   487     IntRect outerRect(0, 0, windowWidth, windowHeight);
       
   488     IntSize innerRadius(borderRadius - borderThickness, borderRadius - borderThickness);
       
   489     IntRect innerRect(borderThickness, borderThickness, windowWidth - borderThickness * 2, windowHeight - borderThickness * 2);
       
   490 
       
   491     context.fillRoundedRect(outerRect, outerRadius, outerRadius, outerRadius, outerRadius, Color(borderColor), DeviceColorSpace);
       
   492     context.setCompositeOperation(CompositeCopy);
       
   493     context.fillRoundedRect(innerRect, innerRadius, innerRadius, innerRadius, innerRadius, Color(backgroundColor), DeviceColorSpace);
       
   494 
       
   495     // Draw the widgets
       
   496     m_playPauseButton.draw(context);
       
   497     m_volumeUpButton.draw(context);
       
   498     m_volumeSliderButton.draw(context);
       
   499     m_volumeDownButton.draw(context);
       
   500     m_timeSliderButton.draw(context);
       
   501     m_exitFullscreenButton.draw(context);
       
   502     m_volumeSlider.draw(context);
       
   503     m_timeSlider.draw(context);
       
   504 
       
   505     // Draw the text strings
       
   506     FontDescription desc;
       
   507 
       
   508     NONCLIENTMETRICS metrics;
       
   509     metrics.cbSize = sizeof(metrics);
       
   510     SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);
       
   511     FontFamily family;
       
   512     family.setFamily(metrics.lfSmCaptionFont.lfFaceName);
       
   513     desc.setFamily(family);
       
   514 
       
   515     desc.setComputedSize(textSize);
       
   516     Font font = Font(desc, 0, 0);
       
   517     font.update(0);
       
   518 
       
   519     String s;
       
   520 
       
   521     // The y positioning of these two text strings is tricky because they are so small. They
       
   522     // are currently positioned relative to the center of the slider and then down the font
       
   523     // height / 4 (which is actually half of font height /2), which positions the center of
       
   524     // the text at the center of the slider.
       
   525     // Left string
       
   526     s = timeToString(currentTime());
       
   527     TextRun leftText(s);
       
   528     context.setFillColor(Color(textColor), DeviceColorSpace);
       
   529     context.drawText(font, leftText, IntPoint(windowWidth / 2 - timeSliderWidth / 2 - margin - font.width(leftText), windowHeight - margin - sliderHeight / 2 + font.height() / 4));
       
   530 
       
   531     // Right string
       
   532     s = timeToString(currentTime() - duration());
       
   533     TextRun rightText(s);
       
   534     context.setFillColor(Color(textColor), DeviceColorSpace);
       
   535     context.drawText(font, rightText, IntPoint(windowWidth / 2 + timeSliderWidth / 2 + margin, windowHeight - margin - sliderHeight / 2 + font.height() / 4));
       
   536 
       
   537     // Copy to the window
       
   538     BLENDFUNCTION blendFunction = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
       
   539     SIZE size = { windowWidth, windowHeight };
       
   540     POINT sourcePoint = {0, 0};
       
   541     POINT destPoint = { m_hudPosition.x(), m_hudPosition.y() };
       
   542     BOOL result = UpdateLayeredWindow(m_hudWindow, 0, &destPoint, &size, bitmapDC, &sourcePoint, 0, &blendFunction, ULW_ALPHA);
       
   543 
       
   544     context.restore();
       
   545 
       
   546     ::DeleteDC(bitmapDC);
       
   547 }
       
   548 
       
   549 LRESULT FullscreenVideoController::hudWndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam)
       
   550 {
       
   551     LONG_PTR longPtr = GetWindowLongPtr(wnd, 0);
       
   552     FullscreenVideoController* controller = reinterpret_cast<FullscreenVideoController*>(longPtr);
       
   553     if (!controller)
       
   554         return DefWindowProc(wnd, message, wParam, lParam);
       
   555 
       
   556     switch (message) {
       
   557     case WM_CHAR:
       
   558         controller->onChar(wParam);
       
   559         break;
       
   560     case WM_KEYDOWN:
       
   561         controller->onKeyDown(wParam);
       
   562         break;
       
   563     case WM_LBUTTONDOWN:
       
   564         controller->onMouseDown(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
       
   565         break;
       
   566     case WM_MOUSEMOVE:
       
   567         controller->onMouseMove(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
       
   568         break;
       
   569     case WM_LBUTTONUP:
       
   570         controller->onMouseUp(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
       
   571         break;
       
   572     }
       
   573 
       
   574     return DefWindowProc(wnd, message, wParam, lParam);
       
   575 }
       
   576 
       
   577 void FullscreenVideoController::onChar(int c)
       
   578 {
       
   579     if (c == VK_ESCAPE) {
       
   580         if (m_mediaElement)
       
   581             m_mediaElement->exitFullscreen();
       
   582     } else if (c == VK_SPACE)
       
   583         togglePlay();
       
   584 }
       
   585 
       
   586 void FullscreenVideoController::onKeyDown(int virtualKey)
       
   587 {
       
   588     if (virtualKey == VK_ESCAPE) {
       
   589         if (m_mediaElement)
       
   590             m_mediaElement->exitFullscreen();
       
   591     }
       
   592 }
       
   593 
       
   594 void FullscreenVideoController::timerFired(Timer<FullscreenVideoController>*)
       
   595 {
       
   596     // Update the time slider
       
   597     m_timeSlider.setValue(currentTime() / duration());
       
   598     draw();
       
   599 }
       
   600 
       
   601 void FullscreenVideoController::onMouseDown(const IntPoint& point)
       
   602 {
       
   603     IntPoint convertedPoint(fullscreenToHUDCoordinates(point));
       
   604 
       
   605     // Don't bother hit testing if we're outside the bounds of the window
       
   606     if (convertedPoint.x() < 0 || convertedPoint.x() >= windowWidth || convertedPoint.y() < 0 || convertedPoint.y() >= windowHeight)
       
   607         return;
       
   608 
       
   609     m_hitWidget = 0;
       
   610     m_movingWindow = false;
       
   611 
       
   612     if (m_playPauseButton.hitTest(convertedPoint))
       
   613         m_hitWidget = &m_playPauseButton;
       
   614     else if (m_exitFullscreenButton.hitTest(convertedPoint))
       
   615         m_hitWidget = &m_exitFullscreenButton;
       
   616     else if (m_volumeUpButton.hitTest(convertedPoint))
       
   617         m_hitWidget = &m_volumeUpButton;
       
   618     else if (m_volumeDownButton.hitTest(convertedPoint))
       
   619         m_hitWidget = &m_volumeDownButton;
       
   620     else if (m_volumeSlider.hitTest(convertedPoint)) {
       
   621         m_hitWidget = &m_volumeSlider;
       
   622         m_volumeSlider.drag(convertedPoint, true);
       
   623         setVolume(m_volumeSlider.value());
       
   624     } else if (m_timeSlider.hitTest(convertedPoint)) {
       
   625         m_hitWidget = &m_timeSlider;
       
   626         m_timeSlider.drag(convertedPoint, true);
       
   627         beginScrubbing();
       
   628         setCurrentTime(m_timeSlider.value() * duration());
       
   629     }
       
   630 
       
   631     // If we did not pick any of our widgets we are starting a window move
       
   632     if (!m_hitWidget) {
       
   633         m_moveOffset = convertedPoint;
       
   634         m_movingWindow = true;
       
   635     }
       
   636 
       
   637     draw();
       
   638 }
       
   639 
       
   640 void FullscreenVideoController::onMouseMove(const IntPoint& point)
       
   641 {
       
   642     IntPoint convertedPoint(fullscreenToHUDCoordinates(point));
       
   643 
       
   644     if (m_hitWidget) {
       
   645         m_hitWidget->drag(convertedPoint, false);
       
   646         if (m_hitWidget == &m_volumeSlider)
       
   647             setVolume(m_volumeSlider.value());
       
   648         else if (m_hitWidget == &m_timeSlider)
       
   649             setCurrentTime(m_timeSlider.value() * duration());
       
   650         draw();
       
   651     } else if (m_movingWindow)
       
   652         m_hudPosition.move(convertedPoint.x() - m_moveOffset.x(), convertedPoint.y() - m_moveOffset.y());
       
   653 }
       
   654 
       
   655 void FullscreenVideoController::onMouseUp(const IntPoint& point)
       
   656 {
       
   657     IntPoint convertedPoint(fullscreenToHUDCoordinates(point));
       
   658     m_movingWindow = false;
       
   659 
       
   660     if (m_hitWidget) {
       
   661         if (m_hitWidget == &m_playPauseButton && m_playPauseButton.hitTest(convertedPoint))
       
   662             togglePlay();
       
   663         else if (m_hitWidget == &m_volumeUpButton && m_volumeUpButton.hitTest(convertedPoint)) {
       
   664             setVolume(1);
       
   665             m_volumeSlider.setValue(1);
       
   666         } else if (m_hitWidget == &m_volumeDownButton && m_volumeDownButton.hitTest(convertedPoint)) {
       
   667             setVolume(0);
       
   668             m_volumeSlider.setValue(0);
       
   669         } else if (m_hitWidget == &m_timeSlider)
       
   670             endScrubbing();
       
   671         else if (m_hitWidget == &m_exitFullscreenButton && m_exitFullscreenButton.hitTest(convertedPoint)) {
       
   672             m_hitWidget = 0;
       
   673             if (m_mediaElement)
       
   674                 m_mediaElement->exitFullscreen();
       
   675             return;
       
   676         }
       
   677     }
       
   678 
       
   679     m_hitWidget = 0;
       
   680     draw();
       
   681 }
       
   682 
       
   683 void FullscreenVideoController::togglePlay()
       
   684 {
       
   685     if (canPlay())
       
   686         play();
       
   687     else
       
   688         pause();
       
   689 
       
   690     m_playPauseButton.setShowAltButton(!canPlay());
       
   691 
       
   692     // Run a timer while the video is playing so we can keep the time
       
   693     // slider and time values up to date.
       
   694     if (!canPlay())
       
   695         m_timer.startRepeating(timerInterval);
       
   696     else
       
   697         m_timer.stop();
       
   698 
       
   699     draw();
       
   700 }
       
   701 
       
   702 #endif