{"id":421,"date":"2013-08-07T12:16:12","date_gmt":"2013-08-07T12:16:12","guid":{"rendered":"http:\/\/strikelimit.co.uk\/m\/?p=421"},"modified":"2013-08-07T13:55:49","modified_gmt":"2013-08-07T13:55:49","slug":"using-awesomium-with-monogame","status":"publish","type":"post","link":"http:\/\/strikelimit.co.uk\/m\/?p=421","title":{"rendered":"Using Awesomium with MonoGame"},"content":{"rendered":"<p>I have been recently working on incorporating the <a title=\"The Awesomium Official Website\" href=\"http:\/\/awesomium.com\/\">Awesomium <\/a>framework into a MonoGame project I am working on, however actually getting it functional was proving a bit of a problem. There are a lot of examples of Awesomium working in XNA out there, but none that I could find for MonoGame &#8211; and the XNA samples proved not to work when ported directly across.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<\/p>\n<p><strong>The Base Class:<\/strong><\/p>\n<p>The first challenge was to create a class that could handle the<em> Awesomium.Core.WebView<\/em> class in a way that allowed you to use it your MonoGame project. Combining several XNA implementations I found, I created this <em>AwesomiumComponent<\/em> class:<\/p>\n<p>&nbsp;<\/p>\n<pre lang=\"CSHARP\">    struct AwesomiumComponent\r\n    {\r\n        public WebView webView;\r\n        public Microsoft.Xna.Framework.Rectangle Rectangle;\r\n\r\n        public AwesomiumComponent(string Source, Microsoft.Xna.Framework.Rectangle rectangle)\r\n        {\r\n            webView = WebCore.CreateWebView(rectangle.Width, rectangle.Height);\r\n            webView.Source = Source.ToUri();\r\n            webView.IsTransparent = true;\r\n\r\n            while (webView.IsLoading)\r\n                WebCore.Update();\r\n\r\n            Rectangle = rectangle;\r\n        }\r\n\r\n        public Texture2D GetTexture(GraphicsDevice graphicsDevice)\r\n        {\r\n            BitmapSurface surface = (BitmapSurface)webView.Surface;\r\n            Bitmap b = new Bitmap(webView.Width, webView.Height, PixelFormat.Format32bppArgb);\r\n            BitmapData bits0 = b.LockBits(\r\n                new System.Drawing.Rectangle(0, 0, webView.Width, webView.Height),\r\n                ImageLockMode.ReadWrite, b.PixelFormat);\r\n            surface.CopyTo(bits0.Scan0, bits0.Stride, 4, false, false);\r\n            b.UnlockBits(bits0);\r\n            int bufferSize = b.Height * b.Width * 4;\r\n            System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(bufferSize);\r\n            b.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);\r\n            Texture2D myTex = Texture2D.FromStream(graphicsDevice, memoryStream);\r\n\r\n            return myTex;\r\n        }\r\n    }<\/pre>\n<p>To initialize the class you pass an address (e.g. http:\/\/www.strikelimit.co.uk, or C:\/\/Users\/Default\/Desktop\/test.html), and the rectangle you wish it to take up on the screen.<\/p>\n<p>The<em> GetTexture()<\/em> method allows you to pass your games <em>GraphicsDevice<\/em> to the component, and get a texture that represents the html page you have set your WebView to, which you can easily drawn using spriteBatch. It first gets the BitmapData from the WebView, and then uses a MemoryStream to pass that data into the MonoGame\/XNA standard of Texture2D.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>Input:<\/strong><\/p>\n<p>I ran into more problems when trying to inject input into the WebView. Mouse input can be added in relatively easily, using the <em>WebView.InjectMouseDown<\/em>,<em> WebView.InjectMouseUp<\/em>, and<em> WebView.InjectMouseMove<\/em> methods to pass input from the MonoGame <em>MouseState<\/em> classes to the game. I came up with the following <em>Update()<\/em> method for the AwesomiumComponent:<\/p>\n<pre lang=\"CSHARP\">        public void Update(MouseState mouseState, MouseState previousMouseState)\r\n        {\r\n            if (mouseState.LeftButton == ButtonState.Pressed &amp;&amp; previousMouseState.LeftButton == ButtonState.Released)\r\n                webView.InjectMouseDown(MouseButton.Left);\r\n            else if (mouseState.LeftButton == ButtonState.Released &amp;&amp; previousMouseState.LeftButton == ButtonState.Pressed)\r\n                webView.InjectMouseUp(MouseButton.Left);\r\n\r\n            if (mouseState.RightButton == ButtonState.Pressed &amp;&amp; previousMouseState.RightButton == ButtonState.Released)\r\n                webView.InjectMouseDown(MouseButton.Right);\r\n            else if (mouseState.RightButton == ButtonState.Released &amp;&amp; previousMouseState.RightButton == ButtonState.Pressed)\r\n                webView.InjectMouseUp(MouseButton.Right);\r\n\r\n            if (mouseState.X != previousMouseState.X || mouseState.Y != previousMouseState.Y)\r\n                webView.InjectMouseMove(mouseState.X - Rectangle.X, mouseState.Y - Rectangle.Y);\r\n        }<\/pre>\n<p>(A note: you may only want to call this function when the mouse is detected inside the components rectangle, so to avoid weird drag selection happening over all the components in your solution.)<\/p>\n<p>Keyboard input is a bit more difficult. Using the MonoGame <em>KeyboardState <\/em>classes would require a potentially large amount of work to get them to give single key press inputs, instead of just saying which keys are currently pressed.<\/p>\n<p>If for some reason you are interested in doing this, the way I would suggest going about it is by comparing the pressed keys in the current and previous <em>KeyboardState<\/em>s and then injecting the keys that are no longer pressed. This however wouldn&#8217;t act like a normal keyboard regarding holding down keys &#8211; they would only be entered once when you stop holding the key.<\/p>\n<p>There are several examples of how to tame the XNA input to give the Windows Messages for key presses that <em>WebView <\/em>class expects you to inject, however due to the use of OpenTK instead of the <em>sort-of-Winforms-base <\/em>that XNA uses, these won&#8217;t work &#8211; indeed as far as I have been able to tell the <a title=\"The MSDN documentation on the SetWindowsHook method\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms644990%28VS.85%29.aspx\"><em>SetWindowsHook<\/em><\/a> method from user32.dll <a title=\"A thread about the subject on the MonoGame discussion forum\" href=\"http:\/\/monogame.codeplex.com\/discussions\/446005\">isn&#8217;t available for use in MonoGame<\/a>.<\/p>\n<p>As suggested in the above linked thread, you can use <em>OpenTK.GameWindow<\/em> events to hook the keyboard input &#8211; and if you use the following methods (just the same as in the above post, but with a bit cut out), you get text input in the normal form:<\/p>\n<pre lang=\"CSHARP\">        protected void HookKeys()\r\n        {\r\n            OpenTK.GameWindow OTKWindow = null;\r\n            Type type = typeof(OpenTKGameWindow);\r\n            System.Reflection.FieldInfo field = type.GetField(\"window\", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);\r\n\r\n            if (field != null)\r\n            {\r\n                OTKWindow = field.GetValue(this.Window) as OpenTK.GameWindow;\r\n            }\r\n\r\n            if (OTKWindow != null)\r\n            {\r\n                OTKWindow.KeyPress += OTKWindow_KeyPress;\r\n            }\r\n        }\r\n\r\n        private void OTKWindow_KeyPress(object sender, OpenTK.KeyPressEventArgs e)\r\n        {\r\n            \/\/ Do something with the input here\r\n        }<\/pre>\n<p>To inject these events into the <em>WebView <\/em>class simply using constructors, it would expect you to have a full Windows Message in the form <em>SetWindowsHook <\/em>would give you, instead you need to use the following code:<\/p>\n<pre lang=\"CSHARP\">                WebKeyboardEvent webKeyboardEvent = new WebKeyboardEvent\r\n                {\r\n                    Type = WebKeyboardEventType.Char,\r\n                    Text = new String(new char[] { e.KeyChar, (char)0, (char)0, (char)0 })\r\n                };\r\n                webView.InjectKeyboardEvent(webKeyboardEvent);<\/pre>\n<p>A note: this code won&#8217;t work with the backspace key, see <a href=\"http:\/\/strikelimit.co.uk\/m\/?p=426\">this follow up post<\/a> for a solution.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have been recently working on incorporating the Awesomium framework into a MonoGame project I am working on, however actually getting it functional was proving a bit of a problem. There are a lot of examples of Awesomium working in XNA out there, but none that I could find for MonoGame &#8211; and the XNA &hellip; <\/p>\n<p><a class=\"more-link btn\" href=\"http:\/\/strikelimit.co.uk\/m\/?p=421\">Continue reading<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[53,3],"tags":[54,4,21,55,56,42,60,33,57],"_links":{"self":[{"href":"http:\/\/strikelimit.co.uk\/m\/index.php?rest_route=\/wp\/v2\/posts\/421"}],"collection":[{"href":"http:\/\/strikelimit.co.uk\/m\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/strikelimit.co.uk\/m\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/strikelimit.co.uk\/m\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/strikelimit.co.uk\/m\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=421"}],"version-history":[{"count":6,"href":"http:\/\/strikelimit.co.uk\/m\/index.php?rest_route=\/wp\/v2\/posts\/421\/revisions"}],"predecessor-version":[{"id":429,"href":"http:\/\/strikelimit.co.uk\/m\/index.php?rest_route=\/wp\/v2\/posts\/421\/revisions\/429"}],"wp:attachment":[{"href":"http:\/\/strikelimit.co.uk\/m\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=421"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/strikelimit.co.uk\/m\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=421"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/strikelimit.co.uk\/m\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=421"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}