1/*
2 * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
3 * Copyright (C) 2006, 2007, 2008, 2013 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "Cursor.h"
29
30#include "BitmapInfo.h"
31#include "HWndDC.h"
32#include "Image.h"
33#include "IntPoint.h"
34#include "SystemInfo.h"
35
36#include <wtf/OwnPtr.h>
37#include <wtf/PassOwnPtr.h>
38#include <wtf/win/GDIObject.h>
39
40#include <windows.h>
41
42#define ALPHA_CURSORS
43
44namespace WebCore {
45
46static PassRefPtr<SharedCursor> createSharedCursor(Image* img, const IntPoint& hotSpot)
47{
48    RefPtr<SharedCursor> impl;
49
50    IntPoint effectiveHotSpot = determineHotSpot(img, hotSpot);
51    static bool doAlpha = windowsVersion() >= WindowsXP;
52    BitmapInfo cursorImage = BitmapInfo::create(IntSize(img->width(), img->height()));
53
54    HWndDC dc(0);
55    auto workingDC = adoptGDIObject(::CreateCompatibleDC(dc));
56    if (doAlpha) {
57        auto hCursor = adoptGDIObject(::CreateDIBSection(dc, (BITMAPINFO *)&cursorImage, DIB_RGB_COLORS, 0, 0, 0));
58        ASSERT(hCursor);
59
60        img->getHBITMAP(hCursor.get());
61        HBITMAP hOldBitmap = (HBITMAP)SelectObject(workingDC.get(), hCursor.get());
62        SetBkMode(workingDC.get(), TRANSPARENT);
63        SelectObject(workingDC.get(), hOldBitmap);
64
65        Vector<unsigned char, 128> maskBits;
66        maskBits.fill(0xff, (img->width() + 7) / 8 * img->height());
67        auto hMask = adoptGDIObject(::CreateBitmap(img->width(), img->height(), 1, 1, maskBits.data()));
68
69        ICONINFO ii;
70        ii.fIcon = FALSE;
71        ii.xHotspot = effectiveHotSpot.x();
72        ii.yHotspot = effectiveHotSpot.y();
73        ii.hbmMask = hMask.get();
74        ii.hbmColor = hCursor.get();
75
76        impl = SharedCursor::create(::CreateIconIndirect(&ii));
77    } else {
78        // Platform doesn't support alpha blended cursors, so we need
79        // to create the mask manually
80        auto andMaskDC = adoptGDIObject(::CreateCompatibleDC(dc));
81        auto xorMaskDC = adoptGDIObject(::CreateCompatibleDC(dc));
82        auto hCursor = adoptGDIObject(::CreateDIBSection(dc, &cursorImage, DIB_RGB_COLORS, 0, 0, 0));
83        ASSERT(hCursor);
84        img->getHBITMAP(hCursor.get());
85        BITMAP cursor;
86        GetObject(hCursor.get(), sizeof(BITMAP), &cursor);
87        auto andMask = adoptGDIObject(::CreateBitmap(cursor.bmWidth, cursor.bmHeight, 1, 1, 0));
88        auto xorMask = adoptGDIObject(::CreateCompatibleBitmap(dc, cursor.bmWidth, cursor.bmHeight));
89        HBITMAP oldCursor = (HBITMAP)SelectObject(workingDC.get(), hCursor.get());
90        HBITMAP oldAndMask = (HBITMAP)SelectObject(andMaskDC.get(), andMask.get());
91        HBITMAP oldXorMask = (HBITMAP)SelectObject(xorMaskDC.get(), xorMask.get());
92
93        SetBkColor(workingDC.get(), RGB(0, 0, 0));
94        BitBlt(andMaskDC.get(), 0, 0, cursor.bmWidth, cursor.bmHeight, workingDC.get(), 0, 0, SRCCOPY);
95
96        SetBkColor(xorMaskDC.get(), RGB(255, 255, 255));
97        SetTextColor(xorMaskDC.get(), RGB(255, 255, 255));
98        BitBlt(xorMaskDC.get(), 0, 0, cursor.bmWidth, cursor.bmHeight, andMaskDC.get(), 0, 0, SRCCOPY);
99        BitBlt(xorMaskDC.get(), 0, 0, cursor.bmWidth, cursor.bmHeight, workingDC.get(), 0, 0, SRCAND);
100
101        SelectObject(workingDC.get(), oldCursor);
102        SelectObject(andMaskDC.get(), oldAndMask);
103        SelectObject(xorMaskDC.get(), oldXorMask);
104
105        ICONINFO icon = {0};
106        icon.fIcon = FALSE;
107        icon.xHotspot = effectiveHotSpot.x();
108        icon.yHotspot = effectiveHotSpot.y();
109        icon.hbmMask = andMask.get();
110        icon.hbmColor = xorMask.get();
111        impl = SharedCursor::create(CreateIconIndirect(&icon));
112    }
113
114    return impl.release();
115}
116
117static PassRefPtr<SharedCursor> loadSharedCursor(HINSTANCE hInstance, LPCWSTR lpCursorName)
118{
119    return SharedCursor::create(::LoadCursorW(hInstance, lpCursorName));
120}
121
122static PassRefPtr<SharedCursor> loadCursorByName(char* name, int x, int y)
123{
124    IntPoint hotSpot(x, y);
125    RefPtr<Image> cursorImage(Image::loadPlatformResource(name));
126    if (cursorImage && !cursorImage->isNull())
127        return createSharedCursor(cursorImage.get(), hotSpot);
128    return loadSharedCursor(0, IDC_ARROW);
129}
130
131void Cursor::ensurePlatformCursor() const
132{
133    if (m_platformCursor)
134        return;
135
136    switch (m_type) {
137    case Cursor::Pointer:
138    case Cursor::Cell:
139    case Cursor::ContextMenu:
140    case Cursor::Alias:
141    case Cursor::Copy:
142    case Cursor::None:
143    case Cursor::Grab:
144    case Cursor::Grabbing:
145        m_platformCursor = loadSharedCursor(0, IDC_ARROW);
146        break;
147    case Cursor::Cross:
148        m_platformCursor = loadSharedCursor(0, IDC_CROSS);
149        break;
150    case Cursor::Hand:
151        m_platformCursor = loadSharedCursor(0, IDC_HAND);
152        break;
153    case Cursor::IBeam:
154        m_platformCursor = loadSharedCursor(0, IDC_IBEAM);
155        break;
156    case Cursor::Wait:
157        m_platformCursor = loadSharedCursor(0, IDC_WAIT);
158        break;
159    case Cursor::Help:
160        m_platformCursor = loadSharedCursor(0, IDC_HELP);
161        break;
162    case Cursor::Move:
163        m_platformCursor = loadSharedCursor(0, IDC_SIZEALL);
164        break;
165    case Cursor::MiddlePanning:
166        m_platformCursor = loadCursorByName("panIcon", 8, 8);
167        break;
168    case Cursor::EastResize:
169        m_platformCursor = loadSharedCursor(0, IDC_SIZEWE);
170        break;
171    case Cursor::EastPanning:
172        m_platformCursor = loadCursorByName("panEastCursor", 7, 7);
173        break;
174    case Cursor::NorthResize:
175        m_platformCursor = loadSharedCursor(0, IDC_SIZENS);
176        break;
177    case Cursor::NorthPanning:
178        m_platformCursor = loadCursorByName("panNorthCursor", 7, 7);
179        break;
180    case Cursor::NorthEastResize:
181        m_platformCursor = loadSharedCursor(0, IDC_SIZENESW);
182        break;
183    case Cursor::NorthEastPanning:
184        m_platformCursor = loadCursorByName("panNorthEastCursor", 7, 7);
185        break;
186    case Cursor::NorthWestResize:
187        m_platformCursor = loadSharedCursor(0, IDC_SIZENWSE);
188        break;
189    case Cursor::NorthWestPanning:
190        m_platformCursor = loadCursorByName("panNorthWestCursor", 7, 7);
191        break;
192    case Cursor::SouthResize:
193        m_platformCursor = loadSharedCursor(0, IDC_SIZENS);
194        break;
195    case Cursor::SouthPanning:
196        m_platformCursor = loadCursorByName("panSouthCursor", 7, 7);
197        break;
198    case Cursor::SouthEastResize:
199        m_platformCursor = loadSharedCursor(0, IDC_SIZENWSE);
200        break;
201    case Cursor::SouthEastPanning:
202        m_platformCursor = loadCursorByName("panSouthEastCursor", 7, 7);
203        break;
204    case Cursor::SouthWestResize:
205        m_platformCursor = loadSharedCursor(0, IDC_SIZENESW);
206        break;
207    case Cursor::SouthWestPanning:
208        m_platformCursor = loadCursorByName("panSouthWestCursor", 7, 7);
209        break;
210    case Cursor::WestResize:
211        m_platformCursor = loadSharedCursor(0, IDC_SIZEWE);
212        break;
213    case Cursor::NorthSouthResize:
214        m_platformCursor = loadSharedCursor(0, IDC_SIZENS);
215        break;
216    case Cursor::EastWestResize:
217        m_platformCursor = loadSharedCursor(0, IDC_SIZEWE);
218        break;
219    case Cursor::WestPanning:
220        m_platformCursor = loadCursorByName("panWestCursor", 7, 7);
221        break;
222    case Cursor::NorthEastSouthWestResize:
223        m_platformCursor = loadSharedCursor(0, IDC_SIZENESW);
224        break;
225    case Cursor::NorthWestSouthEastResize:
226        m_platformCursor = loadSharedCursor(0, IDC_SIZENWSE);
227        break;
228    case Cursor::ColumnResize:
229        // FIXME: Windows does not have a standard column resize cursor <rdar://problem/5018591>
230        m_platformCursor = loadSharedCursor(0, IDC_SIZEWE);
231        break;
232    case Cursor::RowResize:
233        // FIXME: Windows does not have a standard row resize cursor <rdar://problem/5018591>
234        m_platformCursor = loadSharedCursor(0, IDC_SIZENS);
235        break;
236    case Cursor::VerticalText:
237        m_platformCursor = loadCursorByName("verticalTextCursor", 7, 7);
238        break;
239    case Cursor::Progress:
240        m_platformCursor = loadSharedCursor(0, IDC_APPSTARTING);
241        break;
242    case Cursor::NoDrop:
243    case Cursor::NotAllowed:
244        m_platformCursor = loadSharedCursor(0, IDC_NO);
245        break;
246    case Cursor::ZoomIn:
247        m_platformCursor = loadCursorByName("zoomInCursor", 7, 7);
248        break;
249    case Cursor::ZoomOut:
250        m_platformCursor = loadCursorByName("zoomOutCursor", 7, 7);
251        break;
252    case Cursor::Custom:
253        m_platformCursor = createSharedCursor(m_image.get(), m_hotSpot);
254        break;
255    default:
256        ASSERT_NOT_REACHED();
257        m_platformCursor = loadSharedCursor(0, IDC_ARROW);
258        break;
259    }
260}
261
262SharedCursor::~SharedCursor()
263{
264    DestroyIcon(m_nativeCursor);
265}
266
267Cursor::Cursor(const Cursor& other)
268    : m_type(other.m_type)
269    , m_image(other.m_image)
270    , m_hotSpot(other.m_hotSpot)
271    , m_platformCursor(other.m_platformCursor)
272{
273}
274
275Cursor& Cursor::operator=(const Cursor& other)
276{
277    m_type = other.m_type;
278    m_image = other.m_image;
279    m_hotSpot = other.m_hotSpot;
280    m_platformCursor = other.m_platformCursor;
281    return *this;
282}
283
284Cursor::~Cursor()
285{
286}
287
288} // namespace WebCore
289