Remove GraphicsContext::drawConvexPolygon() and GraphicsContext::clipConvexPolygon()
[WebKit-https.git] / Source / WebKit / win / FullscreenVideoController.cpp
1 /*
2  * Copyright (C) 2010, 2013 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 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 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
27 #if ENABLE(VIDEO) && !USE(GSTREAMER) && !USE(MEDIA_FOUNDATION)
28
29 #include "FullscreenVideoController.h"
30
31 #include "WebKitDLL.h"
32 #include "WebView.h"
33 #include <ApplicationServices/ApplicationServices.h>
34 #include <WebCore/BitmapInfo.h>
35 #include <WebCore/Chrome.h>
36 #include <WebCore/FloatRoundedRect.h>
37 #include <WebCore/FontCascade.h>
38 #include <WebCore/FontSelector.h>
39 #include <WebCore/GraphicsContext.h>
40 #include <WebCore/HWndDC.h>
41 #include <WebCore/Page.h>
42 #include <WebCore/PlatformCALayerClient.h>
43 #include <WebCore/PlatformCALayerWin.h>
44 #include <WebCore/TextRun.h>
45 #include <WebKitSystemInterface/WebKitSystemInterface.h>
46 #include <windowsx.h>
47 #include <wtf/StdLibExtras.h>
48
49 using namespace std;
50 using namespace WebCore;
51
52 static const float timerInterval = 0.033;
53
54 // HUD Size
55 static const int windowHeight = 59;
56 static const int windowWidth = 438;
57
58 // Margins and button sizes
59 static const int margin = 9;
60 static const int marginTop = 9;
61 static const int buttonSize = 25;
62 static const int buttonMiniSize = 16;
63 static const int volumeSliderWidth = 50;
64 static const int timeSliderWidth = 310;
65 static const int sliderHeight = 8;
66 static const int volumeSliderButtonSize = 10;
67 static const int timeSliderButtonSize = 8;
68 static const int textSize = 11;
69 static const float initialHUDPositionY = 0.9; // Initial Y position of HUD in percentage from top of screen
70
71 // Background values
72 static const int borderRadius = 12;
73 static const int borderThickness = 2;
74
75 // Colors
76 static const unsigned int backgroundColor = 0xA0202020;
77 static const unsigned int borderColor = 0xFFA0A0A0;
78 static const unsigned int sliderGutterColor = 0xFF141414;
79 static const unsigned int sliderButtonColor = 0xFF808080;
80 static const unsigned int textColor = 0xFFFFFFFF;
81
82 HUDButton::HUDButton(HUDButtonType type, const IntPoint& position)
83     : HUDWidget(IntRect(position, IntSize()))
84     , m_type(type)
85     , m_showAltButton(false)
86 {
87     const char* buttonResource = 0;
88     const char* buttonResourceAlt = 0;
89     switch (m_type) {
90     case PlayPauseButton:
91         buttonResource = "fsVideoPlay";
92         buttonResourceAlt = "fsVideoPause";
93         break;
94     case TimeSliderButton:
95         break;
96     case VolumeUpButton:
97         buttonResource = "fsVideoAudioVolumeHigh";
98         break;
99     case VolumeSliderButton:
100         break;
101     case VolumeDownButton:
102         buttonResource = "fsVideoAudioVolumeLow";
103         break;
104     case ExitFullscreenButton:
105         buttonResource = "fsVideoExitFullscreen";
106         break;
107     }
108
109     if (buttonResource) {
110         m_buttonImage = Image::loadPlatformResource(buttonResource);
111         m_rect.setWidth(m_buttonImage->width());
112         m_rect.setHeight(m_buttonImage->height());
113     }
114     if (buttonResourceAlt)
115         m_buttonImageAlt = Image::loadPlatformResource(buttonResourceAlt);
116 }
117
118 void HUDButton::draw(GraphicsContext& context)
119 {
120     Image* image = (m_showAltButton && m_buttonImageAlt) ? m_buttonImageAlt.get() : m_buttonImage.get();
121     context.drawImage(*image, m_rect.location());
122 }
123
124 HUDSlider::HUDSlider(HUDSliderButtonShape shape, int buttonSize, const IntRect& rect)
125     : HUDWidget(rect)
126     , m_buttonShape(shape)
127     , m_buttonSize(buttonSize)
128     , m_buttonPosition(0)
129     , m_dragStartOffset(0)
130 {
131 }
132
133 void HUDSlider::draw(GraphicsContext& context)
134 {
135     // Draw gutter
136     IntSize radius(m_rect.height() / 2, m_rect.height() / 2);
137     context.fillRoundedRect(FloatRoundedRect(m_rect, radius, radius, radius, radius), Color(sliderGutterColor));
138
139     // Draw button
140     context.setStrokeColor(Color(sliderButtonColor));
141     context.setFillColor(Color(sliderButtonColor));
142
143     if (m_buttonShape == RoundButton) {
144         context.drawEllipse(IntRect(m_rect.location().x() + m_buttonPosition, m_rect.location().y() - (m_buttonSize - m_rect.height()) / 2, m_buttonSize, m_buttonSize));
145         return;
146     }
147
148     // Draw a diamond
149     float half = static_cast<float>(m_buttonSize) / 2;
150
151     Vector<FloatPoint> points = {
152         FloatPoint(m_rect.location().x() + m_buttonPosition + half, m_rect.location().y()),
153         FloatPoint(m_rect.location().x() + m_buttonPosition + m_buttonSize, m_rect.location().y() + half),
154         FloatPoint(m_rect.location().x() + m_buttonPosition + half, m_rect.location().y() + m_buttonSize),
155         FloatPoint(m_rect.location().x() + m_buttonPosition, m_rect.location().y() + half)
156     };
157     context.drawPath(Path::polygonPathFromPoints(points));
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 class FullscreenVideoController::LayerClient : public WebCore::PlatformCALayerClient {
177 public:
178     LayerClient(FullscreenVideoController* parent) : m_parent(parent) { }
179
180 private:
181     virtual void platformCALayerLayoutSublayersOfLayer(PlatformCALayer*);
182     virtual bool platformCALayerRespondsToLayoutChanges() const { return true; }
183
184     virtual void platformCALayerAnimationStarted(CFTimeInterval beginTime) { }
185     virtual GraphicsLayer::CompositingCoordinatesOrientation platformCALayerContentsOrientation() const { return GraphicsLayer::CompositingCoordinatesBottomUp; }
186     virtual void platformCALayerPaintContents(PlatformCALayer*, GraphicsContext&, const FloatRect&) { }
187     virtual bool platformCALayerShowDebugBorders() const { return false; }
188     virtual bool platformCALayerShowRepaintCounter(PlatformCALayer*) const { return false; }
189     virtual int platformCALayerIncrementRepaintCount(PlatformCALayer*) { return 0; }
190
191     virtual bool platformCALayerContentsOpaque() const { return false; }
192     virtual bool platformCALayerDrawsContent() const { return false; }
193     virtual void platformCALayerLayerDidDisplay(PlatformLayer*) { }
194     virtual void platformCALayerDidCreateTiles(const Vector<FloatRect>&) { }
195     virtual float platformCALayerDeviceScaleFactor() const override { return 1; }
196
197     FullscreenVideoController* m_parent;
198 };
199
200 void FullscreenVideoController::LayerClient::platformCALayerLayoutSublayersOfLayer(PlatformCALayer* layer) 
201 {
202     ASSERT_ARG(layer, layer == m_parent->m_rootChild);
203
204     HTMLVideoElement* videoElement = m_parent->m_videoElement.get();
205     if (!videoElement)
206         return;
207
208
209     PlatformCALayer* videoLayer = PlatformCALayer::platformCALayer(videoElement->platformLayer());
210     if (!videoLayer || videoLayer->superlayer() != layer)
211         return;
212
213     FloatRect layerBounds = layer->bounds();
214
215     FloatSize videoSize = videoElement->player()->naturalSize();
216     float scaleFactor;
217     if (videoSize.aspectRatio() > layerBounds.size().aspectRatio())
218         scaleFactor = layerBounds.width() / videoSize.width();
219     else
220         scaleFactor = layerBounds.height() / videoSize.height();
221     videoSize.scale(scaleFactor);
222
223     // Calculate the centered position based on the videoBounds and layerBounds:
224     FloatPoint videoPosition;
225     FloatPoint videoOrigin;
226     videoOrigin.setX((layerBounds.width() - videoSize.width()) * 0.5);
227     videoOrigin.setY((layerBounds.height() - videoSize.height()) * 0.5);
228     videoLayer->setPosition(videoOrigin);
229     videoLayer->setBounds(FloatRect(FloatPoint(), videoSize));
230 }
231
232 FullscreenVideoController::FullscreenVideoController()
233     : m_hudWindow(0)
234     , m_playPauseButton(HUDButton::PlayPauseButton, IntPoint((windowWidth - buttonSize) / 2, marginTop))
235     , m_timeSliderButton(HUDButton::TimeSliderButton, IntPoint(0, 0))
236     , m_volumeUpButton(HUDButton::VolumeUpButton, IntPoint(margin + buttonMiniSize + volumeSliderWidth + buttonMiniSize / 2, marginTop + (buttonSize - buttonMiniSize) / 2))
237     , m_volumeSliderButton(HUDButton::VolumeSliderButton, IntPoint(0, 0))
238     , m_volumeDownButton(HUDButton::VolumeDownButton, IntPoint(margin, marginTop + (buttonSize - buttonMiniSize) / 2))
239     , m_exitFullscreenButton(HUDButton::ExitFullscreenButton, IntPoint(windowWidth - 2 * margin - buttonMiniSize, marginTop + (buttonSize - buttonMiniSize) / 2))
240     , m_volumeSlider(HUDSlider::RoundButton, volumeSliderButtonSize, IntRect(IntPoint(margin + buttonMiniSize, marginTop + (buttonSize - buttonMiniSize) / 2 + buttonMiniSize / 2 - sliderHeight / 2), IntSize(volumeSliderWidth, sliderHeight)))
241     , m_timeSlider(HUDSlider::DiamondButton, timeSliderButtonSize, IntRect(IntPoint(windowWidth / 2 - timeSliderWidth / 2, windowHeight - margin - sliderHeight), IntSize(timeSliderWidth, sliderHeight)))
242     , m_hitWidget(0)
243     , m_movingWindow(false)
244     , m_timer(*this, &FullscreenVideoController::timerFired)
245     , m_layerClient(std::make_unique<LayerClient>(this))
246     , m_rootChild(PlatformCALayerWin::create(PlatformCALayer::LayerTypeLayer, m_layerClient.get()))
247     , m_fullscreenWindow(std::make_unique<MediaPlayerPrivateFullscreenWindow>(static_cast<MediaPlayerPrivateFullscreenClient*>(this)))
248 {
249 }
250
251 FullscreenVideoController::~FullscreenVideoController()
252 {
253     m_rootChild->setOwner(0);
254 }
255
256 void FullscreenVideoController::setVideoElement(HTMLVideoElement* videoElement)
257 {
258     if (videoElement == m_videoElement)
259         return;
260
261     m_videoElement = videoElement;
262     if (!m_videoElement) {
263         // Can't do full-screen, just get out
264         exitFullscreen();
265     }
266 }
267
268 void FullscreenVideoController::enterFullscreen()
269 {
270     if (!m_videoElement)
271         return;
272
273     WebView* webView = kit(m_videoElement->document().page());
274     HWND parentHwnd = webView ? webView->viewWindow() : 0;
275
276     m_fullscreenWindow->createWindow(parentHwnd);
277     ::ShowWindow(m_fullscreenWindow->hwnd(), SW_SHOW);
278     m_fullscreenWindow->setRootChildLayer(m_rootChild);
279
280     PlatformCALayer* videoLayer = PlatformCALayer::platformCALayer(m_videoElement->platformLayer());
281     ASSERT(videoLayer);
282     m_rootChild->appendSublayer(*videoLayer);
283     m_rootChild->setNeedsLayout();
284     m_rootChild->setGeometryFlipped(1);
285
286     RECT windowRect;
287     GetClientRect(m_fullscreenWindow->hwnd(), &windowRect);
288     m_fullscreenSize.setWidth(windowRect.right - windowRect.left);
289     m_fullscreenSize.setHeight(windowRect.bottom - windowRect.top);
290
291     createHUDWindow();
292 }
293
294 void FullscreenVideoController::exitFullscreen()
295 {
296     SetWindowLongPtr(m_hudWindow, 0, 0);
297
298     m_fullscreenWindow = nullptr;
299
300     ASSERT(!IsWindow(m_hudWindow));
301     m_hudWindow = 0;
302
303     // We previously ripped the videoElement's platform layer out
304     // of its orginial layer tree to display it in our fullscreen
305     // window.  Now, we need to get the layer back in its original
306     // tree.
307     // 
308     // As a side effect of setting the player to invisible/visible,
309     // the player's layer will be recreated, and will be picked up 
310     // the next time the layer tree is synched.
311     m_videoElement->player()->setVisible(0);
312     m_videoElement->player()->setVisible(1);
313 }
314
315 bool FullscreenVideoController::canPlay() const
316 {
317     return m_videoElement && m_videoElement->canPlay();
318 }
319
320 void FullscreenVideoController::play()
321 {
322     if (m_videoElement)
323         m_videoElement->play();
324 }
325
326 void FullscreenVideoController::pause()
327 {
328     if (m_videoElement)
329         m_videoElement->pause();
330 }
331
332 float FullscreenVideoController::volume() const
333 {
334     return m_videoElement ? m_videoElement->volume() : 0;
335 }
336
337 void FullscreenVideoController::setVolume(float volume)
338 {
339     if (m_videoElement) {
340         ExceptionCode ec;
341         m_videoElement->setVolume(volume, ec);
342     }
343 }
344
345 float FullscreenVideoController::currentTime() const
346 {
347     return m_videoElement ? m_videoElement->currentTime() : 0;
348 }
349
350 void FullscreenVideoController::setCurrentTime(float value)
351 {
352     if (m_videoElement)
353         m_videoElement->setCurrentTime(value);
354 }
355
356 float FullscreenVideoController::duration() const
357 {
358     return m_videoElement ? m_videoElement->duration() : 0;
359 }
360
361 void FullscreenVideoController::beginScrubbing()
362 {
363     if (m_videoElement) 
364         m_videoElement->beginScrubbing();
365 }
366
367 void FullscreenVideoController::endScrubbing()
368 {
369     if (m_videoElement) 
370         m_videoElement->endScrubbing();
371 }
372
373 LRESULT FullscreenVideoController::fullscreenClientWndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam)
374 {
375     switch (message) {
376     case WM_CHAR:
377         onChar(wParam);
378         break;
379     case WM_KEYDOWN:
380         onKeyDown(wParam);
381         break;
382     case WM_LBUTTONDOWN:
383         onMouseDown(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
384         break;
385     case WM_MOUSEMOVE:
386         onMouseMove(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
387         break;
388     case WM_LBUTTONUP:
389         onMouseUp(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
390         break;
391     }
392
393     return DefWindowProc(wnd, message, wParam, lParam);
394 }
395
396 static const LPCWSTR fullscreenVideeoHUDWindowClassName = L"fullscreenVideeoHUDWindowClass";
397
398 void FullscreenVideoController::registerHUDWindowClass()
399 {
400     static bool haveRegisteredHUDWindowClass;
401     if (haveRegisteredHUDWindowClass)
402         return;
403
404     haveRegisteredHUDWindowClass = true;
405
406     WNDCLASSEX wcex;
407
408     wcex.cbSize = sizeof(WNDCLASSEX);
409
410     wcex.style = CS_HREDRAW | CS_VREDRAW;
411     wcex.lpfnWndProc = hudWndProc;
412     wcex.cbClsExtra = 0;
413     wcex.cbWndExtra = sizeof(FullscreenVideoController*);
414     wcex.hInstance = gInstance;
415     wcex.hIcon = 0;
416     wcex.hCursor = LoadCursor(0, IDC_ARROW);
417     wcex.hbrBackground = 0;
418     wcex.lpszMenuName = 0;
419     wcex.lpszClassName = fullscreenVideeoHUDWindowClassName;
420     wcex.hIconSm = 0;
421
422     RegisterClassEx(&wcex);
423 }
424
425 void FullscreenVideoController::createHUDWindow()
426 {
427     m_hudPosition.setX((m_fullscreenSize.width() - windowWidth) / 2);
428     m_hudPosition.setY(m_fullscreenSize.height() * initialHUDPositionY - windowHeight / 2);
429
430     // Local variable that will hold the returned pixels. No need to cleanup this value. It
431     // will get cleaned up when m_bitmap is destroyed in the dtor
432     void* pixels;
433     BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(IntSize(windowWidth, windowHeight));
434     m_bitmap = adoptGDIObject(::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0));
435
436     // Dirty the window so the HUD draws
437     RECT clearRect = { m_hudPosition.x(), m_hudPosition.y(), m_hudPosition.x() + windowWidth, m_hudPosition.y() + windowHeight };
438     InvalidateRect(m_fullscreenWindow->hwnd(), &clearRect, true);
439
440     m_playPauseButton.setShowAltButton(!canPlay());
441     m_volumeSlider.setValue(volume());
442     m_timeSlider.setValue(currentTime() / duration());
443
444     if (!canPlay())
445         m_timer.startRepeating(timerInterval);
446
447     registerHUDWindowClass();
448
449     m_hudWindow = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, 
450         fullscreenVideeoHUDWindowClassName, 0, WS_POPUP | WS_VISIBLE,
451         m_hudPosition.x(), m_hudPosition.y(), 0, 0, m_fullscreenWindow->hwnd(), 0, gInstance, 0);
452     ASSERT(::IsWindow(m_hudWindow));
453     SetWindowLongPtr(m_hudWindow, 0, reinterpret_cast<LONG_PTR>(this));
454
455     draw();
456 }
457
458 static String timeToString(float time)
459 {
460     if (!std::isfinite(time))
461         time = 0;
462     int seconds = fabsf(time); 
463     int hours = seconds / (60 * 60);
464     int minutes = (seconds / 60) % 60;
465     seconds %= 60;
466
467     if (hours) {
468         if (hours > 9)
469             return String::format("%s%02d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds);
470         return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds);
471     }
472
473     return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, seconds);
474 }
475
476 void FullscreenVideoController::draw()
477 {
478     auto bitmapDC = adoptGDIObject(::CreateCompatibleDC(HWndDC(m_hudWindow)));
479     HGDIOBJ oldBitmap = SelectObject(bitmapDC.get(), m_bitmap.get());
480
481     GraphicsContext context(bitmapDC.get(), 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(FloatRoundedRect(outerRect, outerRadius, outerRadius, outerRadius, outerRadius), Color(borderColor));
492     context.setCompositeOperation(CompositeCopy);
493     context.fillRoundedRect(FloatRoundedRect(innerRect, innerRadius, innerRadius, innerRadius, innerRadius), Color(backgroundColor));
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     FontCascadeDescription desc;
507
508     NONCLIENTMETRICS metrics;
509     metrics.cbSize = sizeof(metrics);
510     SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);
511     desc.setOneFamily(metrics.lfSmCaptionFont.lfFaceName);
512
513     desc.setComputedSize(textSize);
514     FontCascade font = FontCascade(desc, 0, 0);
515     font.update(0);
516
517     String s;
518
519     // The y positioning of these two text strings is tricky because they are so small. They
520     // are currently positioned relative to the center of the slider and then down the font
521     // height / 4 (which is actually half of font height /2), which positions the center of
522     // the text at the center of the slider.
523     // Left string
524     s = timeToString(currentTime());
525     int fontHeight = font.fontMetrics().height();
526     TextRun leftText(s);
527     context.setFillColor(Color(textColor));
528     context.drawText(font, leftText, IntPoint(windowWidth / 2 - timeSliderWidth / 2 - margin - font.width(leftText), windowHeight - margin - sliderHeight / 2 + fontHeight / 4));
529
530     // Right string
531     s = timeToString(currentTime() - duration());
532     TextRun rightText(s);
533     context.setFillColor(Color(textColor));
534     context.drawText(font, rightText, IntPoint(windowWidth / 2 + timeSliderWidth / 2 + margin, windowHeight - margin - sliderHeight / 2 + fontHeight / 4));
535
536     // Copy to the window
537     BLENDFUNCTION blendFunction = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
538     SIZE size = { windowWidth, windowHeight };
539     POINT sourcePoint = {0, 0};
540     POINT destPoint = { m_hudPosition.x(), m_hudPosition.y() };
541     BOOL result = UpdateLayeredWindow(m_hudWindow, 0, &destPoint, &size, bitmapDC.get(), &sourcePoint, 0, &blendFunction, ULW_ALPHA);
542
543     context.restore();
544
545     ::SelectObject(bitmapDC.get(), oldBitmap);
546 }
547
548 LRESULT FullscreenVideoController::hudWndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam)
549 {
550     LONG_PTR longPtr = GetWindowLongPtr(wnd, 0);
551     FullscreenVideoController* controller = reinterpret_cast<FullscreenVideoController*>(longPtr);
552     if (!controller)
553         return DefWindowProc(wnd, message, wParam, lParam);
554
555     switch (message) {
556     case WM_CHAR:
557         controller->onChar(wParam);
558         break;
559     case WM_KEYDOWN:
560         controller->onKeyDown(wParam);
561         break;
562     case WM_LBUTTONDOWN:
563         controller->onMouseDown(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
564         break;
565     case WM_MOUSEMOVE:
566         controller->onMouseMove(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
567         break;
568     case WM_LBUTTONUP:
569         controller->onMouseUp(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
570         break;
571     }
572
573     return DefWindowProc(wnd, message, wParam, lParam);
574 }
575
576 void FullscreenVideoController::onChar(int c)
577 {
578     if (c == VK_ESCAPE) {
579         if (m_videoElement)
580             m_videoElement->exitFullscreen();
581     } else if (c == VK_SPACE)
582         togglePlay();
583 }
584
585 void FullscreenVideoController::onKeyDown(int virtualKey)
586 {
587     if (virtualKey == VK_ESCAPE) {
588         if (m_videoElement)
589             m_videoElement->exitFullscreen();
590     }
591 }
592
593 void FullscreenVideoController::timerFired()
594 {
595     // Update the time slider
596     m_timeSlider.setValue(currentTime() / duration());
597     draw();
598 }
599
600 void FullscreenVideoController::onMouseDown(const IntPoint& point)
601 {
602     IntPoint convertedPoint(fullscreenToHUDCoordinates(point));
603
604     // Don't bother hit testing if we're outside the bounds of the window
605     if (convertedPoint.x() < 0 || convertedPoint.x() >= windowWidth || convertedPoint.y() < 0 || convertedPoint.y() >= windowHeight)
606         return;
607
608     m_hitWidget = 0;
609     m_movingWindow = false;
610
611     if (m_playPauseButton.hitTest(convertedPoint))
612         m_hitWidget = &m_playPauseButton;
613     else if (m_exitFullscreenButton.hitTest(convertedPoint))
614         m_hitWidget = &m_exitFullscreenButton;
615     else if (m_volumeUpButton.hitTest(convertedPoint))
616         m_hitWidget = &m_volumeUpButton;
617     else if (m_volumeDownButton.hitTest(convertedPoint))
618         m_hitWidget = &m_volumeDownButton;
619     else if (m_volumeSlider.hitTest(convertedPoint)) {
620         m_hitWidget = &m_volumeSlider;
621         m_volumeSlider.drag(convertedPoint, true);
622         setVolume(m_volumeSlider.value());
623     } else if (m_timeSlider.hitTest(convertedPoint)) {
624         m_hitWidget = &m_timeSlider;
625         m_timeSlider.drag(convertedPoint, true);
626         beginScrubbing();
627         setCurrentTime(m_timeSlider.value() * duration());
628     }
629
630     // If we did not pick any of our widgets we are starting a window move
631     if (!m_hitWidget) {
632         m_moveOffset = convertedPoint;
633         m_movingWindow = true;
634     }
635
636     draw();
637 }
638
639 void FullscreenVideoController::onMouseMove(const IntPoint& point)
640 {
641     IntPoint convertedPoint(fullscreenToHUDCoordinates(point));
642
643     if (m_hitWidget) {
644         m_hitWidget->drag(convertedPoint, false);
645         if (m_hitWidget == &m_volumeSlider)
646             setVolume(m_volumeSlider.value());
647         else if (m_hitWidget == &m_timeSlider)
648             setCurrentTime(m_timeSlider.value() * duration());
649         draw();
650     } else if (m_movingWindow)
651         m_hudPosition.move(convertedPoint.x() - m_moveOffset.x(), convertedPoint.y() - m_moveOffset.y());
652 }
653
654 void FullscreenVideoController::onMouseUp(const IntPoint& point)
655 {
656     IntPoint convertedPoint(fullscreenToHUDCoordinates(point));
657     m_movingWindow = false;
658
659     if (m_hitWidget) {
660         if (m_hitWidget == &m_playPauseButton && m_playPauseButton.hitTest(convertedPoint))
661             togglePlay();
662         else if (m_hitWidget == &m_volumeUpButton && m_volumeUpButton.hitTest(convertedPoint)) {
663             setVolume(1);
664             m_volumeSlider.setValue(1);
665         } else if (m_hitWidget == &m_volumeDownButton && m_volumeDownButton.hitTest(convertedPoint)) {
666             setVolume(0);
667             m_volumeSlider.setValue(0);
668         } else if (m_hitWidget == &m_timeSlider)
669             endScrubbing();
670         else if (m_hitWidget == &m_exitFullscreenButton && m_exitFullscreenButton.hitTest(convertedPoint)) {
671             m_hitWidget = 0;
672             if (m_videoElement)
673                 m_videoElement->exitFullscreen();
674             return;
675         }
676     }
677
678     m_hitWidget = 0;
679     draw();
680 }
681
682 void FullscreenVideoController::togglePlay()
683 {
684     if (canPlay())
685         play();
686     else
687         pause();
688
689     m_playPauseButton.setShowAltButton(!canPlay());
690
691     // Run a timer while the video is playing so we can keep the time
692     // slider and time values up to date.
693     if (!canPlay())
694         m_timer.startRepeating(timerInterval);
695     else
696         m_timer.stop();
697
698     draw();
699 }
700
701 #endif