Skip to content

Instantly share code, notes, and snippets.

@TinkerWorX
Created May 13, 2012 22:20
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TinkerWorX/2690490 to your computer and use it in GitHub Desktop.
Save TinkerWorX/2690490 to your computer and use it in GitHub Desktop.
An XNA compatible Awesomium component.
JCPM_MWX.Awesomium.Xna
{
public class AwesomiumComponent : DrawableGameComponent
{
private delegate Int32 ProcessMessagesDelegate(Int32 code, Int32 wParam, ref Message lParam);
private static class User32
{
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetWindowsHookEx(Int32 windowsHookId, ProcessMessagesDelegate function, IntPtr mod, Int32 threadId);
[DllImport("user32.dll", SetLastError = true)]
internal static extern Int32 UnhookWindowsHookEx(IntPtr hook);
[DllImport("user32.dll", SetLastError = true)]
internal static extern Int32 CallNextHookEx(IntPtr hook, Int32 code, Int32 wParam, ref Message lParam);
[DllImport("user32.dll", SetLastError = true)]
internal static extern Boolean TranslateMessage(ref Message message);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr FindWindow(String className, String windowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int RegisterWindowMessage(String msg);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(HandleRef hWnd, Int32 msg, Int32 wParam, Int32 lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern bool SystemParametersInfo(Int32 nAction, Int32 nParam, ref Int32 value, Int32 ignore);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int GetSystemMetrics(Int32 nIndex);
}
private static class Kernel32
{
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern Int32 GetCurrentThreadId();
}
private static class SystemMetrics
{
internal static Int32 MouseWheelScrollDelta
{
get
{
return 120;
}
}
internal static Int32 MouseWheelScrollLines
{
get
{
var scrollLines = 0;
if (User32.GetSystemMetrics(75) == 0)
{
var hwnd = User32.FindWindow("MouseZ", "Magellan MSWHEEL");
if (hwnd != IntPtr.Zero)
{
var windowMessage = User32.RegisterWindowMessage("MSH_SCROLL_LINES_MSG");
scrollLines = (Int32)User32.SendMessage(new HandleRef(null, hwnd), windowMessage, 0, 0);
if (scrollLines != 0)
{
return scrollLines;
}
}
return 3;
}
User32.SystemParametersInfo(104, 0, ref scrollLines, 0);
return scrollLines;
}
}
}
private enum WindowsMessage
{
KeyDown = 0x0100,
KeyUp = 0x0101,
Char = 0x0102,
MouseMove = 0x0200,
LeftButtonDown = 0x0201,
LeftButtonUp = 0x0202,
LeftButtonDoubleClick = 0x0203,
RightButtonDown = 0x0204,
RightButtonUp = 0x0205,
RightButtonDoubleClick = 0x0206,
MiddleButtonDown = 0x0207,
MiddleButtonUp = 0x0208,
MiddleButtonDoubleClick = 0x0209,
MouseWheel = 0x020A,
}
private struct Message
{
internal IntPtr HWnd;
internal Int32 Msg;
internal IntPtr WParam;
internal IntPtr LParam;
internal IntPtr Result;
}
private readonly IntPtr hookHandle;
private readonly ProcessMessagesDelegate processMessages;
private Byte[] imageBytes;
private Rectangle area;
private Rectangle? newArea;
private Boolean resizing;
public AwesomiumComponent(Game game, Rectangle area)
: base(game)
{
this.area = area;
this.WebView = WebCore.CreateWebView(this.area.Width, this.area.Height);
this.WebView.ResizeComplete += this.WebView_ResizeComplete;
this.WebView.FlushAlpha = false;
this.WebView.IsTransparent = true;
this.processMessages = this.ProcessMessages;
// Create the message hook.
this.hookHandle = User32.SetWindowsHookEx(3, this.processMessages, IntPtr.Zero, Kernel32.GetCurrentThreadId());
}
public AwesomiumComponent(Game game)
: base(game)
{
this.area = Rectangle.Empty;
this.WebView = WebCore.CreateWebView(this.area.Width, this.area.Height);
this.WebView.ResizeComplete += this.WebView_ResizeComplete;
this.WebView.FlushAlpha = false;
this.WebView.IsTransparent = true;
this.processMessages = this.ProcessMessages;
// Create the message hook.
this.hookHandle = User32.SetWindowsHookEx(3, this.processMessages, IntPtr.Zero, Kernel32.GetCurrentThreadId());
}
~AwesomiumComponent()
{
// Remove the message hook.
if (this.hookHandle != IntPtr.Zero)
User32.UnhookWindowsHookEx(this.hookHandle);
}
public Texture2D WebViewTexture { get; private set; }
public WebView WebView { get; private set; }
public Rectangle Area
{
get { return this.area; }
set
{
this.newArea = value;
}
}
private Int32 ProcessMessages(Int32 code, Int32 wParam, ref Message lParam)
{
if (code == 0 && wParam == 1)
{
switch ((WindowsMessage)lParam.Msg)
{
case WindowsMessage.KeyDown:
case WindowsMessage.KeyUp:
case WindowsMessage.Char:
this.WebView.InjectKeyboardEventWin(lParam.Msg, (Int32)lParam.WParam, (Int32)lParam.LParam);
break;
case WindowsMessage.MouseMove:
var mouse = Mouse.GetState();
this.WebView.InjectMouseMove(mouse.X - this.area.X, mouse.Y - this.area.Y);
break;
case WindowsMessage.LeftButtonDown: this.WebView.InjectMouseDown(MouseButton.Left); break;
case WindowsMessage.LeftButtonUp: this.WebView.InjectMouseUp(MouseButton.Left); break;
case WindowsMessage.LeftButtonDoubleClick: this.WebView.InjectMouseDown(MouseButton.Left); break;
case WindowsMessage.RightButtonDown: this.WebView.InjectMouseDown(MouseButton.Right); break;
case WindowsMessage.RightButtonUp: this.WebView.InjectMouseUp(MouseButton.Right); break;
case WindowsMessage.RightButtonDoubleClick: this.WebView.InjectMouseDown(MouseButton.Right); break;
case WindowsMessage.MiddleButtonDown: this.WebView.InjectMouseDown(MouseButton.Middle); break;
case WindowsMessage.MiddleButtonUp: this.WebView.InjectMouseUp(MouseButton.Middle); break;
case WindowsMessage.MiddleButtonDoubleClick: this.WebView.InjectMouseDown(MouseButton.Middle); break;
case WindowsMessage.MouseWheel:
var delta = (((Int32)lParam.WParam) >> 16);
this.WebView.InjectMouseWheel(delta / SystemMetrics.MouseWheelScrollDelta * 16 * SystemMetrics.MouseWheelScrollLines);
break;
//case (WindowsMessage)0xC328: break;
//default:
// Console.WriteLine("{{default}}: {0}", lParam);
// break;
}
User32.TranslateMessage(ref lParam);
}
return User32.CallNextHookEx(IntPtr.Zero, code, wParam, ref lParam);
}
private void WebView_ResizeComplete(Object sender, ResizeEventArgs e)
{
this.resizing = false;
}
protected override void LoadContent()
{
if (this.area.IsEmpty)
{
this.area = this.GraphicsDevice.Viewport.Bounds;
this.newArea = this.GraphicsDevice.Viewport.Bounds;
}
this.WebViewTexture = new Texture2D(this.Game.GraphicsDevice, this.area.Width, this.area.Height, false, SurfaceFormat.Color);
this.imageBytes = new Byte[this.area.Width * 4 * this.area.Height];
}
public override void Update(GameTime gameTime)
{
if (this.newArea.HasValue && !this.resizing && gameTime.TotalGameTime.TotalSeconds > 0.10f)
{
this.area = this.newArea.Value;
if (this.area.IsEmpty)
this.area = this.GraphicsDevice.Viewport.Bounds;
this.WebView.Resize(this.area.Width, this.area.Height);
this.WebViewTexture = new Texture2D(this.Game.GraphicsDevice, this.area.Width, this.area.Height, false, SurfaceFormat.Color);
this.imageBytes = new Byte[this.area.Width * 4 * this.area.Height];
this.resizing = true;
this.newArea = null;
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
if (this.WebView.IsDirty)
{
var renderBuffer = this.WebView.Render();
unsafe
{
// This part saves us from double copying everything.
fixed (Byte* imagePtr = this.imageBytes)
{
renderBuffer.CopyTo((IntPtr)imagePtr, renderBuffer.Width * 4, 4, true);
}
}
this.WebViewTexture.SetData(this.imageBytes);
}
base.Draw(gameTime);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment