1/*
2 * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
3 * Copyright (C) 2006, 2007, 2008 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 COMPUTER, 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 COMPUTER, 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
39#include <windows.h>
40
41#define ALPHA_CURSORS
42
43namespace WebCore {
44
45static PassRefPtr<SharedCursor> createSharedCursor(Image* img, const IntPoint& hotSpot)
46{
47    RefPtr<SharedCursor> impl;
48
49    IntPoint effectiveHotSpot = determineHotSpot(img, hotSpot);
50    static bool doAlpha = windowsVersion() >= WindowsXP;
51    BitmapInfo cursorImage = BitmapInfo::create(IntSize(img->width(), img->height()));
52
53    HWndDC dc(0);
54    HDC workingDC = CreateCompatibleDC(dc);
55    if (doAlpha) {
56        OwnPtr<HBITMAP> hCursor = adoptPtr(CreateDIBSection(dc, (BITMAPINFO *)&cursorImage, DIB_RGB_COLORS, 0, 0, 0));
57        ASSERT(hCursor);
58
59        img->getHBITMAP(hCursor.get());
60        HBITMAP hOldBitmap = (HBITMAP)SelectObject(workingDC, hCursor.get());
61        SetBkMode(workingDC, TRANSPARENT);
62        SelectObject(workingDC, hOldBitmap);
63
64        Vector<unsigned char, 128> maskBits;
65        maskBits.fill(0xff, (img->width() + 7) / 8 * img->height());
66        OwnPtr<HBITMAP> hMask = adoptPtr(CreateBitmap(img->width(), img->height(), 1, 1, maskBits.data()));
67
68        ICONINFO ii;
69        ii.fIcon = FALSE;
70        ii.xHotspot = effectiveHotSpot.x();
71        ii.yHotspot = effectiveHotSpot.y();
72        ii.hbmMask = hMask.get();
73        ii.hbmColor = hCursor.get();
74
75        impl = SharedCursor::create(CreateIconIndirect(&ii));
76    } else {
77        // Platform doesn't support alpha blended cursors, so we need
78        // to create the mask manually
79        HDC andMaskDC = CreateCompatibleDC(dc);
80        HDC xorMaskDC = CreateCompatibleDC(dc);
81        OwnPtr<HBITMAP> hCursor = adoptPtr(CreateDIBSection(dc, &cursorImage, DIB_RGB_COLORS, 0, 0, 0));
82        ASSERT(hCursor);
83        img->getHBITMAP(hCursor.get());
84        BITMAP cursor;
85        GetObject(hCursor.get(), sizeof(BITMAP), &cursor);
86        OwnPtr<HBITMAP> andMask = adoptPtr(CreateBitmap(cursor.bmWidth, cursor.bmHeight, 1, 1, NULL));
87        OwnPtr<HBITMAP> xorMask = adoptPtr(CreateCompatibleBitmap(dc, cursor.bmWidth, cursor.bmHeight));
88        HBITMAP oldCursor = (HBITMAP)SelectObject(workingDC, hCursor.get());
89        HBITMAP oldAndMask = (HBITMAP)SelectObject(andMaskDC, andMask.get());
90        HBITMAP oldXorMask = (HBITMAP)SelectObject(xorMaskDC, xorMask.get());
91
92        SetBkColor(workingDC, RGB(0,0,0));
93        BitBlt(andMaskDC, 0, 0, cursor.bmWidth, cursor.bmHeight, workingDC, 0, 0, SRCCOPY);
94
95        SetBkColor(xorMaskDC, RGB(255, 255, 255));
96        SetTextColor(xorMaskDC, RGB(255, 255, 255));
97        BitBlt(xorMaskDC, 0, 0, cursor.bmWidth, cursor.bmHeight, andMaskDC, 0, 0, SRCCOPY);
98        BitBlt(xorMaskDC, 0, 0, cursor.bmWidth, cursor.bmHeight, workingDC, 0,0, SRCAND);
99
100        SelectObject(workingDC, oldCursor);
101        SelectObject(andMaskDC, oldAndMask);
102        SelectObject(xorMaskDC, oldXorMask);
103
104        ICONINFO icon = {0};
105        icon.fIcon = FALSE;
106        icon.xHotspot = effectiveHotSpot.x();
107        icon.yHotspot = effectiveHotSpot.y();
108        icon.hbmMask = andMask.get();
109        icon.hbmColor = xorMask.get();
110        impl = SharedCursor::create(CreateIconIndirect(&icon));
111
112        DeleteDC(xorMaskDC);
113        DeleteDC(andMaskDC);
114    }
115    DeleteDC(workingDC);
116
117    return impl.release();
118}
119
120static PassRefPtr<SharedCursor> loadSharedCursor(HINSTANCE hInstance, LPCWSTR lpCursorName)
121{
122    return SharedCursor::create(::LoadCursorW(hInstance, lpCursorName));
123}
124
125static PassRefPtr<SharedCursor> loadCursorByName(char* name, int x, int y)
126{
127    IntPoint hotSpot(x, y);
128    RefPtr<Image> cursorImage(Image::loadPlatformResource(name));
129    if (cursorImage && !cursorImage->isNull())
130        return createSharedCursor(cursorImage.get(), hotSpot);
131    return loadSharedCursor(0, IDC_ARROW);
132}
133
134void Cursor::ensurePlatformCursor() const
135{
136    if (m_platformCursor)
137        return;
138
139    switch (m_type) {
140    case Cursor::Pointer:
141    case Cursor::Cell:
142    case Cursor::ContextMenu:
143    case Cursor::Alias:
144    case Cursor::Copy:
145    case Cursor::None:
146    case Cursor::Grab:
147    case Cursor::Grabbing:
148        m_platformCursor = loadSharedCursor(0, IDC_ARROW);
149        break;
150    case Cursor::Cross:
151        m_platformCursor = loadSharedCursor(0, IDC_CROSS);
152        break;
153    case Cursor::Hand:
154        m_platformCursor = loadSharedCursor(0, IDC_HAND);
155        break;
156    case Cursor::IBeam:
157        m_platformCursor = loadSharedCursor(0, IDC_IBEAM);
158        break;
159    case Cursor::Wait:
160        m_platformCursor = loadSharedCursor(0, IDC_WAIT);
161        break;
162    case Cursor::Help:
163        m_platformCursor = loadSharedCursor(0, IDC_HELP);
164        break;
165    case Cursor::Move:
166        m_platformCursor = loadSharedCursor(0, IDC_SIZEALL);
167        break;
168    case Cursor::MiddlePanning:
169        m_platformCursor = loadCursorByName("panIcon", 8, 8);
170        break;
171    case Cursor::EastResize:
172        m_platformCursor = loadSharedCursor(0, IDC_SIZEWE);
173        break;
174    case Cursor::EastPanning:
175        m_platformCursor = loadCursorByName("panEastCursor", 7, 7);
176        break;
177    case Cursor::NorthResize:
178        m_platformCursor = loadSharedCursor(0, IDC_SIZENS);
179        break;
180    case Cursor::NorthPanning:
181        m_platformCursor = loadCursorByName("panNorthCursor", 7, 7);
182        break;
183    case Cursor::NorthEastResize:
184        m_platformCursor = loadSharedCursor(0, IDC_SIZENESW);
185        break;
186    case Cursor::NorthEastPanning:
187        m_platformCursor = loadCursorByName("panNorthEastCursor", 7, 7);
188        break;
189    case Cursor::NorthWestResize:
190        m_platformCursor = loadSharedCursor(0, IDC_SIZENWSE);
191        break;
192    case Cursor::NorthWestPanning:
193        m_platformCursor = loadCursorByName("panNorthWestCursor", 7, 7);
194        break;
195    case Cursor::SouthResize:
196        m_platformCursor = loadSharedCursor(0, IDC_SIZENS);
197        break;
198    case Cursor::SouthPanning:
199        m_platformCursor = loadCursorByName("panSouthCursor", 7, 7);
200        break;
201    case Cursor::SouthEastResize:
202        m_platformCursor = loadSharedCursor(0, IDC_SIZENWSE);
203        break;
204    case Cursor::SouthEastPanning:
205        m_platformCursor = loadCursorByName("panSouthEastCursor", 7, 7);
206        break;
207    case Cursor::SouthWestResize:
208        m_platformCursor = loadSharedCursor(0, IDC_SIZENESW);
209        break;
210    case Cursor::SouthWestPanning:
211        m_platformCursor = loadCursorByName("panSouthWestCursor", 7, 7);
212        break;
213    case Cursor::WestResize:
214        m_platformCursor = loadSharedCursor(0, IDC_SIZEWE);
215        break;
216    case Cursor::NorthSouthResize:
217        m_platformCursor = loadSharedCursor(0, IDC_SIZENS);
218        break;
219    case Cursor::EastWestResize:
220        m_platformCursor = loadSharedCursor(0, IDC_SIZEWE);
221        break;
222    case Cursor::WestPanning:
223        m_platformCursor = loadCursorByName("panWestCursor", 7, 7);
224        break;
225    case Cursor::NorthEastSouthWestResize:
226        m_platformCursor = loadSharedCursor(0, IDC_SIZENESW);
227        break;
228    case Cursor::NorthWestSouthEastResize:
229        m_platformCursor = loadSharedCursor(0, IDC_SIZENWSE);
230        break;
231    case Cursor::ColumnResize:
232        // FIXME: Windows does not have a standard column resize cursor <rdar://problem/5018591>
233        m_platformCursor = loadSharedCursor(0, IDC_SIZEWE);
234        break;
235    case Cursor::RowResize:
236        // FIXME: Windows does not have a standard row resize cursor <rdar://problem/5018591>
237        m_platformCursor = loadSharedCursor(0, IDC_SIZENS);
238        break;
239    case Cursor::VerticalText:
240        m_platformCursor = loadCursorByName("verticalTextCursor", 7, 7);
241        break;
242    case Cursor::Progress:
243        m_platformCursor = loadSharedCursor(0, IDC_APPSTARTING);
244        break;
245    case Cursor::NoDrop:
246    case Cursor::NotAllowed:
247        m_platformCursor = loadSharedCursor(0, IDC_NO);
248        break;
249    case Cursor::ZoomIn:
250        m_platformCursor = loadCursorByName("zoomInCursor", 7, 7);
251        break;
252    case Cursor::ZoomOut:
253        m_platformCursor = loadCursorByName("zoomOutCursor", 7, 7);
254        break;
255    case Cursor::Custom:
256        m_platformCursor = createSharedCursor(m_image.get(), m_hotSpot);
257        break;
258    default:
259        ASSERT_NOT_REACHED();
260        m_platformCursor = loadSharedCursor(0, IDC_ARROW);
261        break;
262    }
263}
264
265SharedCursor::~SharedCursor()
266{
267    DestroyIcon(m_nativeCursor);
268}
269
270Cursor::Cursor(const Cursor& other)
271    : m_type(other.m_type)
272    , m_image(other.m_image)
273    , m_hotSpot(other.m_hotSpot)
274    , m_platformCursor(other.m_platformCursor)
275{
276}
277
278Cursor& Cursor::operator=(const Cursor& other)
279{
280    m_type = other.m_type;
281    m_image = other.m_image;
282    m_hotSpot = other.m_hotSpot;
283    m_platformCursor = other.m_platformCursor;
284    return *this;
285}
286
287Cursor::~Cursor()
288{
289}
290
291} // namespace WebCore
292