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