Skip to content

Commit df562dd

Browse files
committed
WPF - Workaround for Cursors not loading correctly
- For cursors that are embedded in libcef.dll the OnCursorChange method is passing in IntPtr.Zero so we're unable to load the cursor. This commit adds a manual mapping of CursorType to resource Id and loads the cursors manually - Adds a basic test to assert the cursors load, should validate if the resource ids change This needs to be fixed upstream https://bitbucket.org/chromiumembedded/cef/issues/3270/osr-pan-icon-missing-when-pressing-the Test url https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values Issue #4021
1 parent 77b20c7 commit df562dd

File tree

6 files changed

+169
-1
lines changed

6 files changed

+169
-1
lines changed

CefSharp.Core.Runtime/NativeMethodWrapper.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,23 @@ namespace CefSharp
6060
SetWindowLongPtr(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_NOACTIVATE);
6161
}
6262
}
63+
64+
IntPtr NativeMethodWrapper::LoadCursorFromLibCef(int resourceIdentifier)
65+
{
66+
auto moduleName = L"libcef.dll";
67+
68+
HMODULE hModule = GetModuleHandle(moduleName);
69+
70+
if (hModule == nullptr)
71+
{
72+
return IntPtr::Zero;
73+
}
74+
75+
auto lpCursorName = MAKEINTRESOURCE(resourceIdentifier);
76+
77+
auto cursor = LoadCursor(hModule, lpCursorName);
78+
79+
return static_cast<IntPtr>(cursor);
80+
}
6381
}
6482
}

CefSharp.Core.Runtime/NativeMethodWrapper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ namespace CefSharp
2323
static void SetWindowPosition(IntPtr handle, int x, int y, int width, int height);
2424
static void SetWindowParent(IntPtr child, IntPtr newParent);
2525
static void RemoveExNoActivateStyle(IntPtr browserHwnd);
26+
static IntPtr LoadCursorFromLibCef(int resourceIdentifier);
2627
};
2728
}
2829
}

CefSharp.Core/NativeMethodWrapper.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,10 @@ public static void RemoveExNoActivateStyle(IntPtr browserHwnd)
3838
{
3939
CefSharp.Core.NativeMethodWrapper.RemoveExNoActivateStyle(browserHwnd);
4040
}
41+
42+
public static IntPtr LoadCursorFromLibCef(int resourceIdentifier)
43+
{
44+
return CefSharp.Core.NativeMethodWrapper.LoadCursorFromLibCef(resourceIdentifier);
45+
}
4146
}
4247
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright © 2022 The CefSharp Authors. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4+
5+
using System;
6+
using CefSharp.Enums;
7+
using CefSharp.Wpf.Internals;
8+
using Xunit;
9+
10+
namespace CefSharp.Test.Wpf
11+
{
12+
//NOTE: All Test classes must be part of this collection as it manages the Cef Initialize/Shutdown lifecycle
13+
/// <summary>
14+
/// Issue #4021
15+
/// Validate Cursor resource Ids
16+
/// </summary>
17+
[Collection(CefSharpFixtureCollection.Key)]
18+
public class EmbeddedCursorLoadTests
19+
{
20+
[Theory]
21+
[InlineData(CursorType.Alias)]
22+
[InlineData(CursorType.Cell)]
23+
[InlineData(CursorType.ColumnResize)]
24+
[InlineData(CursorType.Copy)]
25+
[InlineData(CursorType.Grab)]
26+
[InlineData(CursorType.Grabbing)]
27+
[InlineData(CursorType.EastPanning)]
28+
[InlineData(CursorType.MiddlePanning)]
29+
[InlineData(CursorType.MiddlePanningHorizontal)]
30+
[InlineData(CursorType.MiddlePanningVertical)]
31+
[InlineData(CursorType.NorthPanning)]
32+
[InlineData(CursorType.NortheastPanning)]
33+
[InlineData(CursorType.NorthwestPanning)]
34+
[InlineData(CursorType.SouthPanning)]
35+
[InlineData(CursorType.SoutheastPanning)]
36+
[InlineData(CursorType.SouthwestPanning)]
37+
[InlineData(CursorType.WestPanning)]
38+
[InlineData(CursorType.RowResize)]
39+
[InlineData(CursorType.VerticalText)]
40+
[InlineData(CursorType.ZoomIn)]
41+
[InlineData(CursorType.ZoomOut)]
42+
public void CanGetEmbeddedCursors(CursorType cursorType)
43+
{
44+
var actual = EmbeddedCursor.TryLoadCursor(cursorType, out IntPtr cursorPtr);
45+
46+
Assert.True(actual);
47+
Assert.NotEqual(IntPtr.Zero, cursorPtr);
48+
}
49+
}
50+
}

CefSharp.Wpf/ChromiumWebBrowser.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,16 @@ protected virtual void OnCursorChange(IntPtr handle, CursorType type, CursorInfo
10071007
{
10081008
UiThreadRunAsync(() =>
10091009
{
1010-
Cursor = CursorInteropHelper.Create(new SafeFileHandle(handle, ownsHandle: false));
1010+
//Workaround for upstream issue
1011+
//See #4021
1012+
if (EmbeddedCursor.TryLoadCursor(type, out IntPtr cursorHandle))
1013+
{
1014+
Cursor = CursorInteropHelper.Create(new SafeFileHandle(cursorHandle, ownsHandle: false));
1015+
}
1016+
else
1017+
{
1018+
Cursor = CursorInteropHelper.Create(new SafeFileHandle(handle, ownsHandle: false));
1019+
}
10111020
});
10121021
}
10131022
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright © 2022 The CefSharp Authors. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using CefSharp.Enums;
8+
9+
namespace CefSharp.Wpf.Internals
10+
{
11+
// Workaround for upstream issue
12+
// It's possible resources Ids might change between versions, so this might need to be updated
13+
// for different builds, will need to test after upgrading
14+
// See #4021
15+
public static class EmbeddedCursor
16+
{
17+
//const int IDC_ALIAS = 35610;
18+
//const int IDC_CELL = 35611;
19+
//const int IDC_COLRESIZE = 35612;
20+
//const int IDC_COPYCUR = 35613;
21+
//const int IDC_HAND_GRAB = 35614;
22+
//const int IDC_HAND_GRABBING = 35615;
23+
//const int IDC_PAN_EAST = 35616;
24+
//const int IDC_PAN_MIDDLE = 35617;
25+
//const int IDC_PAN_MIDDLE_HORIZONTAL = 35618;
26+
//const int IDC_PAN_MIDDLE_VERTICAL = 35619;
27+
//const int IDC_PAN_NORTH = 35620;
28+
//const int IDC_PAN_NORTH_EAST = 35621;
29+
//const int IDC_PAN_NORTH_WEST = 35622;
30+
//const int IDC_PAN_SOUTH = 35623;
31+
//const int IDC_PAN_SOUTH_EAST = 35624;
32+
//const int IDC_PAN_SOUTH_WEST = 35625;
33+
//const int IDC_PAN_WEST = 35626;
34+
//const int IDC_ROWRESIZE = 35627;
35+
//const int IDC_VERTICALTEXT = 35628;
36+
//const int IDC_ZOOMIN = 35629;
37+
//const int IDC_ZOOMOUT = 35630;
38+
39+
private static Dictionary<CursorType, int> EmbeddedCursors = new Dictionary<CursorType, int>
40+
{
41+
{ CursorType.Alias, 35610 },
42+
{ CursorType.Cell, 35611 },
43+
{ CursorType.ColumnResize, 35612 },
44+
{ CursorType.Copy, 35613 },
45+
{ CursorType.Grab, 35614 },
46+
{ CursorType.Grabbing, 35615 },
47+
{ CursorType.EastPanning, 35616 },
48+
{ CursorType.MiddlePanning, 35617 },
49+
{ CursorType.MiddlePanningHorizontal, 35618 },
50+
{ CursorType.MiddlePanningVertical, 35619 },
51+
{ CursorType.NorthPanning, 35620 },
52+
{ CursorType.NortheastPanning, 35621 },
53+
{ CursorType.NorthwestPanning, 35622 },
54+
{ CursorType.SouthPanning, 35623 },
55+
{ CursorType.SoutheastPanning, 35624 },
56+
{ CursorType.SouthwestPanning, 35625 },
57+
{ CursorType.WestPanning, 35626 },
58+
{ CursorType.RowResize, 35627 },
59+
{ CursorType.VerticalText, 35628 },
60+
{ CursorType.ZoomIn, 35629 },
61+
{ CursorType.ZoomOut, 35630 }
62+
};
63+
64+
public static bool TryLoadCursor(CursorType cursorType, out IntPtr cursor)
65+
{
66+
cursor = IntPtr.Zero;
67+
68+
try
69+
{
70+
if (EmbeddedCursors.TryGetValue(cursorType, out int key))
71+
{
72+
cursor = NativeMethodWrapper.LoadCursorFromLibCef(key);
73+
74+
return cursor != IntPtr.Zero;
75+
}
76+
}
77+
catch (Exception)
78+
{
79+
80+
}
81+
82+
return false;
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)