Browse Source

Make it into a library

Miguel de Icaza 7 years ago
parent
commit
152126473a

+ 61 - 3
README.md

@@ -4,16 +4,74 @@ This is a simple UI toolkit for .NET.
 
 It is an updated version of
 [gui.cs](https://github.com/mono/mono-curses/blob/master/gui.cs) that
-I wrote for [mono-curses](https://github.com/mono/mono-curses)
+I wrote for [mono-curses](https://github.com/mono/mono-curses).
+
+The toolkit contains various controls (labesl, text entry, buttons,
+radio buttons, checkboxes, dialog boxes, windows, menus) for building
+text user interfaces, a main loop, is designed to work on Curses and
+the [Windows
+Console](https://github.com/migueldeicaza/gui.cs/issues/27), works
+well on both color and monochrome terminals and has mouse support on
+terminal emulators that support it.
 
 # API Documentation
 
 Go to the [API documentation](https://migueldeicaza.github.io/gui.cs/api/Terminal.html) for details.
 
+# Sample Usage
+
+```
+using Terminal.Gui;
+
+class Demo {
+    static void Main ()
+    {
+        Application.Init ();
+        var top = Application.Top;
+
+	// Creates the top-level window to show
+        var win = new Window (new Rect (0, 1, top.Frame.Width, top.Frame.Height-1), "MyApp");
+        top.Add (win);
+
+	// Creates a menubar, the item "New" has a help menu.
+        var menu = new MenuBar (new MenuBarItem [] {
+            new MenuBarItem ("_File", new MenuItem [] {
+                new MenuItem ("_New", "Creates new file", NewFile),
+                new MenuItem ("_Close", "", () => Close ()),
+                new MenuItem ("_Quit", "", () => { if (Quit ()) top.Running = false; })
+            }),
+            new MenuBarItem ("_Edit", new MenuItem [] {
+                new MenuItem ("_Copy", "", null),
+                new MenuItem ("C_ut", "", null),
+                new MenuItem ("_Paste", "", null)
+            })
+        });
+        top.Add (menu);
+
+	// Add some controls
+	win.Add (
+            new Label (3, 2, "Login: "),
+            new TextField (14, 2, 40, ""),
+            new Label (3, 4, "Password: "),
+            new TextField (14, 4, 40, "") { Secret = true },
+            new CheckBox (3, 6, "Remember me"),
+            new RadioGroup (3, 8, new [] { "_Personal", "_Company" }),
+            new Button (3, 14, "Ok"),
+            new Button (10, 14, "Cancel"),
+            new Label (3, 18, "Press ESC and 9 to activate the menubar"));
+
+        ShowEntries (win);
+
+        Application.Run ();
+    }
+}
+```
+
+This shows a UI like this:
+
 # Running and Building
 
-To run this, you will need a peer checkout of mono-ncurses to this
-directory.   I should make a NuGet package at some point.
+Open the solution and run the sample program.
 
 # Input Handling
 

+ 0 - 0
Core.cs → Terminal.Gui/Core.cs


+ 0 - 0
Driver.cs → Terminal.Gui/Driver.cs


+ 0 - 0
Event.cs → Terminal.Gui/Event.cs


+ 13 - 0
Terminal.Gui/MonoCurses/README.md

@@ -0,0 +1,13 @@
+This directory contains a copy of the MonoCurses binding from:
+
+http://github.com/mono/mono-curses
+
+The source code has been exported in a way that the MonoCurses
+API is kept private and does not surface to the user, this is
+done with the command:
+
+```
+	make publish-to-gui
+```
+
+In the MonoCurses package

+ 382 - 0
Terminal.Gui/MonoCurses/binding.cs

@@ -0,0 +1,382 @@
+
+//
+// binding.cs.in: Core binding for curses.
+//
+// Authors:
+//   Miguel de Icaza ([email protected])
+//
+// Copyright (C) 2007 Novell (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Unix.Terminal {
+
+	internal partial class Curses {
+
+		[StructLayout (LayoutKind.Sequential)]
+		internal struct MouseEvent {
+			public short ID;
+			public int X, Y, Z;
+			public Event ButtonState;
+		}
+
+#region Screen initialization
+
+		[DllImport ("ncurses", EntryPoint="initscr")]
+		extern static internal IntPtr real_initscr ();
+		static int lines, cols;
+
+		static Window main_window;
+		static IntPtr curses_handle, curscr_ptr, lines_ptr, cols_ptr;
+
+		static void FindNCurses ()
+		{
+			if (File.Exists ("/usr/lib/libncurses.dylib"))
+				curses_handle = dlopen ("libncurses.dylib", 0);
+			else
+				curses_handle = dlopen ("libncurses.so", 0);
+			
+			if (curses_handle == IntPtr.Zero)
+				throw new Exception ("Could not dlopen ncurses");
+
+			stdscr = read_static_ptr ("stdscr");
+			curscr_ptr = get_ptr ("curscr");
+			lines_ptr = get_ptr ("LINES");
+			cols_ptr = get_ptr ("COLS");
+		}
+		
+		static public Window initscr ()
+		{
+			FindNCurses ();
+			
+			main_window = new Window (real_initscr ());
+			try {
+				console_sharp_get_dims (out lines, out cols);
+			} catch (DllNotFoundException){
+				endwin ();
+				Console.Error.WriteLine ("Unable to find the @MONO_CURSES@ native library\n" + 
+							 "this is different than the managed mono-curses.dll\n\n" +
+							 "Typically you need to install to a LD_LIBRARY_PATH directory\n" +
+							 "or DYLD_LIBRARY_PATH directory or run /sbin/ldconfig");
+				Environment.Exit (1);
+			}
+			return main_window;
+		}
+
+		public static int Lines {	
+			get {
+				return lines;
+			}
+		}
+
+		public static int Cols {
+			get {
+				return cols;
+			}
+		}
+
+		//
+		// Returns true if the window changed since the last invocation, as a
+		// side effect, the Lines and Cols properties are updated
+		//
+		public static bool CheckWinChange ()
+		{
+			int l, c;
+			
+			console_sharp_get_dims (out l, out c);
+			if (l != lines || c != cols){
+				lines = l;
+				cols = c;
+				return true;
+			}
+			return false;
+		}
+		
+		[DllImport ("ncurses")]
+		extern static public int endwin ();
+
+		[DllImport ("ncurses")]
+		extern static public bool isendwin ();
+
+		//
+		// Screen operations are flagged as internal, as we need to
+		// catch all changes so we can update newscr, curscr, stdscr
+		//
+		[DllImport ("ncurses")]
+		extern static public IntPtr internal_newterm (string type, IntPtr file_outfd, IntPtr file_infd);
+
+		[DllImport ("ncurses")]
+		extern static public IntPtr internal_set_term (IntPtr newscreen);
+
+		[DllImport ("ncurses")]
+	        extern static internal void internal_delscreen (IntPtr sp);
+#endregion
+
+#region Input Options
+		[DllImport ("ncurses")]
+		extern static public int cbreak ();
+		
+		[DllImport ("ncurses")]
+		extern static public int nocbreak ();
+		
+		[DllImport ("ncurses")]
+		extern static public int echo ();
+		
+		[DllImport ("ncurses")]
+		extern static public int noecho ();
+		
+		[DllImport ("ncurses")]
+		extern static public int halfdelay (int t);
+
+		[DllImport ("ncurses")]
+		extern static public int raw ();
+
+		[DllImport ("ncurses")]
+		extern static public int noraw ();
+		
+		[DllImport ("ncurses")]
+		extern static public void noqiflush ();
+		
+		[DllImport ("ncurses")]
+		extern static public void qiflush ();
+
+		[DllImport ("ncurses")]
+		extern static public int typeahead (IntPtr fd);
+
+		[DllImport ("ncurses")]
+		extern static public int timeout (int delay);
+
+		//
+		// Internal, as they are exposed in Window
+		//
+		[DllImport ("ncurses")]
+		extern static internal int wtimeout (IntPtr win, int delay);
+	       
+		[DllImport ("ncurses")]
+		extern static internal int notimeout (IntPtr win, bool bf);
+
+		[DllImport ("ncurses")]
+		extern static internal int keypad (IntPtr win, bool bf);
+		
+		[DllImport ("ncurses")]
+		extern static internal int meta (IntPtr win, bool bf);
+		
+		[DllImport ("ncurses")]
+		extern static internal int intrflush (IntPtr win, bool bf);
+#endregion
+
+#region Output Options
+		[DllImport ("ncurses")]
+		extern internal static int clearok (IntPtr win, bool bf);
+		[DllImport ("ncurses")]
+		extern internal static int idlok (IntPtr win, bool bf);
+		[DllImport ("ncurses")]
+		extern internal static void idcok (IntPtr win, bool bf);
+		[DllImport ("ncurses")]
+		extern internal static void immedok (IntPtr win, bool bf);
+		[DllImport ("ncurses")]
+		extern internal static int leaveok (IntPtr win, bool bf);
+		[DllImport ("ncurses")]
+		extern internal static int wsetscrreg (IntPtr win, int top, int bot);
+		[DllImport ("ncurses")]
+		extern internal static int scrollok (IntPtr win, bool bf);
+		
+		[DllImport ("ncurses")]
+		extern public static int nl();
+		[DllImport ("ncurses")]
+		extern public static int nonl();
+		[DllImport ("ncurses")]
+		extern public static int setscrreg (int top, int bot);
+		
+#endregion
+
+#region refresh functions
+
+		[DllImport ("ncurses")]
+		extern public static int refresh ();
+		[DllImport ("ncurses")]
+		extern public static int doupdate();
+
+		[DllImport ("ncurses")]
+		extern internal static int wrefresh (IntPtr win);
+		[DllImport ("ncurses")]
+		extern internal static int redrawwin (IntPtr win);
+		[DllImport ("ncurses")]
+		extern internal static int wredrawwin (IntPtr win, int beg_line, int num_lines);
+		[DllImport ("ncurses")]
+		extern internal static int wnoutrefresh (IntPtr win);
+#endregion
+
+#region Output
+		[DllImport ("ncurses")]
+		extern public static int move (int line, int col);
+
+		[DllImport ("ncurses", EntryPoint="addch")]
+		extern internal static int _addch (int ch);
+		
+		[DllImport ("ncurses")]
+		extern public static int addstr (string s);
+
+		public static int addstr (string format, params object [] args)
+		{
+			var s = string.Format (format, args);
+			return addstr (s);
+		}
+
+		static char [] r = new char [1];
+
+		//
+		// Have to wrap the native addch, as it can not
+		// display unicode characters, we have to use addstr
+		// for that.   but we need addch to render special ACS
+		// characters
+		//
+		public static int addch (int ch)
+		{
+			if (ch < 127 || ch > 0xffff )
+				return _addch (ch);
+			char c = (char) ch;
+			return addstr (new String (c, 1));
+		}
+		
+		[DllImport ("ncurses")]
+		extern internal static int wmove (IntPtr win, int line, int col);
+
+		[DllImport ("ncurses")]
+		extern internal static int waddch (IntPtr win, int ch);
+#endregion
+
+#region Attributes
+		[DllImport ("ncurses")]
+		extern public static int attron (int attrs);
+		[DllImport ("ncurses")]
+		extern public static int attroff (int attrs);
+		[DllImport ("ncurses")]
+		extern public static int attrset (int attrs);
+#endregion
+
+#region Input
+		[DllImport ("ncurses")]
+		extern public static int getch ();
+		
+		[DllImport ("ncurses")]
+		extern public static int get_wch (out int sequence);
+
+		[DllImport ("ncurses")]
+		extern public static int ungetch (int ch);
+
+		[DllImport ("ncurses")]
+		extern public static int mvgetch (int y, int x);
+#endregion
+		
+#region Colors
+		[DllImport ("ncurses")]
+		extern internal static bool has_colors ();
+		public static bool HasColors => has_colors ();
+
+		[DllImport ("ncurses")]
+		extern internal static int start_color ();
+		public static int StartColor () => start_color ();
+
+		[DllImport ("ncurses")]
+		extern internal static int init_pair (short pair, short f, short b);
+		public static int InitColorPair (short pair, short foreground, short background) => init_pair (pair, foreground, background);
+
+		[DllImport ("ncurses")]
+		extern internal static int use_default_colors ();
+		public static int UseDefaultColors () => use_default_colors ();
+
+		[DllImport ("ncurses")]
+		extern internal static int COLOR_PAIRS();
+		public static int ColorPairs => COLOR_PAIRS();
+		
+		
+#endregion
+		
+		[DllImport ("libc")]
+		extern static IntPtr dlopen (string file, int mode);
+
+		[DllImport ("libc")]
+		extern static IntPtr dlsym (IntPtr handle, string symbol);
+
+		static IntPtr stdscr;
+
+		static IntPtr get_ptr (string key)
+		{
+			var ptr = dlsym (curses_handle, key);
+			if (ptr == IntPtr.Zero)
+				throw new Exception ("Could not load the key " + key);
+			return ptr;
+		}
+		
+		internal static IntPtr read_static_ptr (string key)
+		{
+			var ptr = get_ptr (key);
+			return Marshal.ReadIntPtr (ptr);
+		}
+
+		internal static IntPtr console_sharp_get_stdscr () => stdscr;
+		
+		
+#region Helpers
+		internal static IntPtr console_sharp_get_curscr ()
+		{
+			return Marshal.ReadIntPtr (curscr_ptr);
+		}
+
+		internal static void console_sharp_get_dims (out int lines, out int cols)
+		{
+			lines = Marshal.ReadInt32 (lines_ptr);
+			cols = Marshal.ReadInt32 (cols_ptr);
+		}
+
+		[DllImport ("ncurses", EntryPoint="mousemask")]
+		extern static IntPtr call_mousemask (IntPtr newmask, out IntPtr oldmask);
+		
+		public static Event mousemask (Event newmask, out Event oldmask)
+		{
+			IntPtr e;
+			var ret = (Event) call_mousemask ((IntPtr) newmask, out e);
+			oldmask = (Event) e;
+			return ret;
+		}
+
+		[DllImport ("ncurses")]
+		public extern static uint getmouse (out MouseEvent ev);
+
+		[DllImport ("ncurses")]
+		public extern static uint ungetmouse (ref MouseEvent ev);
+#endregion
+
+		// We encode ESC + char (what Alt-char generates) as 0x2000 + char
+		public const int KeyAlt = 0x2000;
+
+		static public int IsAlt (int key)
+		{
+			if ((key & KeyAlt) != 0)
+				return key & ~KeyAlt;
+			return 0;
+		}
+	}
+}

+ 92 - 0
Terminal.Gui/MonoCurses/constants.cs

@@ -0,0 +1,92 @@
+/*
+ * This file is autogenerated by the attrib.c program, do not edit 
+ */
+
+using System;
+
+namespace Unix.Terminal {
+	internal partial class Curses {
+		public const int A_NORMAL = unchecked((int)0x0);
+		public const int A_STANDOUT = unchecked((int)0x10000);
+		public const int A_UNDERLINE = unchecked((int)0x20000);
+		public const int A_REVERSE = unchecked((int)0x40000);
+		public const int A_BLINK = unchecked((int)0x80000);
+		public const int A_DIM = unchecked((int)0x100000);
+		public const int A_BOLD = unchecked((int)0x200000);
+		public const int A_PROTECT = unchecked((int)0x1000000);
+		public const int A_INVIS = unchecked((int)0x800000);
+		public const int ACS_LLCORNER = unchecked((int)0x40006d);
+		public const int ACS_LRCORNER = unchecked((int)0x40006a);
+		public const int ACS_HLINE = unchecked((int)0x400071);
+		public const int ACS_ULCORNER = unchecked((int)0x40006c);
+		public const int ACS_URCORNER = unchecked((int)0x40006b);
+		public const int ACS_VLINE = unchecked((int)0x400078);
+		public const int COLOR_BLACK = unchecked((int)0x0);
+		public const int COLOR_RED = unchecked((int)0x1);
+		public const int COLOR_GREEN = unchecked((int)0x2);
+		public const int COLOR_YELLOW = unchecked((int)0x3);
+		public const int COLOR_BLUE = unchecked((int)0x4);
+		public const int COLOR_MAGENTA = unchecked((int)0x5);
+		public const int COLOR_CYAN = unchecked((int)0x6);
+		public const int COLOR_WHITE = unchecked((int)0x7);
+		public const int KEY_CODE_YES = unchecked((int)0x100);
+		internal enum Event : long {
+			Button1Pressed = unchecked((int)0x2),
+			Button1Released = unchecked((int)0x1),
+			Button1Clicked = unchecked((int)0x4),
+			Button1DoubleClicked = unchecked((int)0x8),
+			Button1TripleClicked = unchecked((int)0x10),
+			Button2Pressed = unchecked((int)0x80),
+			Button2Released = unchecked((int)0x40),
+			Button2Clicked = unchecked((int)0x100),
+			Button2DoubleClicked = unchecked((int)0x200),
+			Button2TrippleClicked = unchecked((int)0x400),
+			Button3Pressed = unchecked((int)0x2000),
+			Button3Released = unchecked((int)0x1000),
+			Button3Clicked = unchecked((int)0x4000),
+			Button3DoubleClicked = unchecked((int)0x8000),
+			Button3TripleClicked = unchecked((int)0x10000),
+			Button4Pressed = unchecked((int)0x80000),
+			Button4Released = unchecked((int)0x40000),
+			Button4Clicked = unchecked((int)0x100000),
+			Button4DoubleClicked = unchecked((int)0x200000),
+			Button4TripleClicked = unchecked((int)0x400000),
+			ButtonShift = unchecked((int)0x2000000),
+			ButtonCtrl = unchecked((int)0x1000000),
+			ButtonAlt = unchecked((int)0x4000000),
+			ReportMousePosition = unchecked((int)0x8000000),
+			AllEvents = unchecked((int)0x7ffffff),
+		}
+		public const int ERR = unchecked((int)0xffffffff);
+		public const int KeyBackspace = unchecked((int)0x107);
+		public const int KeyUp = unchecked((int)0x103);
+		public const int KeyDown = unchecked((int)0x102);
+		public const int KeyLeft = unchecked((int)0x104);
+		public const int KeyRight = unchecked((int)0x105);
+		public const int KeyNPage = unchecked((int)0x152);
+		public const int KeyPPage = unchecked((int)0x153);
+		public const int KeyHome = unchecked((int)0x106);
+		public const int KeyMouse = unchecked((int)0x199);
+		public const int KeyEnd = unchecked((int)0x168);
+		public const int KeyDeleteChar = unchecked((int)0x14a);
+		public const int KeyInsertChar = unchecked((int)0x14b);
+		public const int KeyBackTab = unchecked((int)0x161);
+		public const int KeyF1 = unchecked((int)0x109);
+		public const int KeyF2 = unchecked((int)0x10a);
+		public const int KeyF3 = unchecked((int)0x10b);
+		public const int KeyF4 = unchecked((int)0x10c);
+		public const int KeyF5 = unchecked((int)0x10d);
+		public const int KeyF6 = unchecked((int)0x10e);
+		public const int KeyF7 = unchecked((int)0x10f);
+		public const int KeyF8 = unchecked((int)0x110);
+		public const int KeyF9 = unchecked((int)0x111);
+		public const int KeyF10 = unchecked((int)0x112);
+		public const int KeyResize = unchecked((int)0x19a);
+
+
+		static public int ColorPair(int n){
+			return 0 + n * 256;
+		}
+
+	}
+}

+ 172 - 0
Terminal.Gui/MonoCurses/handles.cs

@@ -0,0 +1,172 @@
+//
+// handles.cs: OO wrappers for some curses objects
+//
+// Authors:
+//   Miguel de Icaza ([email protected])
+//
+// Copyright (C) 2007 Novell (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Runtime.InteropServices;
+
+namespace Unix.Terminal {
+
+	internal partial class Curses {
+		internal class Window {
+			public readonly IntPtr Handle;
+			static Window curscr;
+			static Window stdscr;
+	
+			static Window ()
+			{
+				Curses.initscr ();
+				stdscr = new Window (Curses.console_sharp_get_stdscr ());
+				curscr = new Window (Curses.console_sharp_get_curscr ());
+			}
+			
+			internal Window (IntPtr handle) 
+			{
+				Handle = handle;
+			}
+			
+			static public Window Standard {
+				get {
+					return stdscr;
+				}
+			}
+	
+			static public Window Current {
+				get {
+					return curscr;
+				}
+			}
+	
+			
+			public int wtimeout (int delay)
+			{
+				return Curses.wtimeout (Handle, delay);
+			}
+	
+			public int notimeout (bool bf)
+			{
+				return Curses.notimeout (Handle, bf);
+			}
+	
+			public int keypad (bool bf)
+			{
+				return Curses.keypad (Handle, bf);
+			}
+	
+			public int meta (bool bf)
+			{
+				return Curses.meta (Handle, bf);
+			}
+	
+			public int intrflush (bool bf)
+			{
+				return Curses.intrflush (Handle, bf);
+			}
+	
+			public int clearok (bool bf)
+			{
+				return Curses.clearok (Handle, bf);
+			}
+			
+			public int idlok (bool bf)
+			{
+				return Curses.idlok (Handle, bf);
+			}
+			
+			public void idcok (bool bf)
+			{
+				Curses.idcok (Handle, bf);
+			}
+			
+			public void immedok (bool bf)
+			{
+				Curses.immedok (Handle, bf);
+			}
+			
+			public int leaveok (bool bf)
+			{
+				return Curses.leaveok (Handle, bf);
+			}
+			
+			public int setscrreg (int top, int bot)
+			{
+				return Curses.wsetscrreg (Handle, top, bot);
+			}
+			
+			public int scrollok (bool bf)
+			{
+				return Curses.scrollok (Handle, bf);
+			}
+			
+			public int wrefresh ()
+			{
+				return Curses.wrefresh (Handle);
+			}
+	
+			public int redrawwin ()
+			{
+				return Curses.redrawwin (Handle);
+			}
+			
+			public int wredrawwin (int beg_line, int num_lines)
+			{
+				return Curses.wredrawwin (Handle, beg_line, num_lines);
+			}
+	
+			public int wnoutrefresh ()
+			{
+				return Curses.wnoutrefresh (Handle);
+			}
+	
+			public int move (int line, int col)
+			{
+				return Curses.wmove (Handle, line, col);
+			}
+	
+			public int addch (char ch)
+			{
+				return Curses.waddch (Handle, ch);
+			}
+	
+			public int refresh ()
+			{
+				return Curses.wrefresh (Handle);
+			}
+		}
+	
+	 	// Currently unused, to do later
+	 	internal class Screen {
+	 		public readonly IntPtr Handle;
+	 		
+	 		internal Screen (IntPtr handle)
+	 		{
+	 			Handle = handle;
+	 		}
+	 	}
+	 
+	}
+
+}

+ 362 - 0
Terminal.Gui/MonoCurses/mainloop.cs

@@ -0,0 +1,362 @@
+//
+// mainloop.cs: Simple managed mainloop implementation.
+//
+// Authors:
+//   Miguel de Icaza ([email protected])
+//
+// Copyright (C) 2011 Novell (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using Mono.Unix.Native;
+using System.Collections.Generic;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Mono.Terminal {
+
+	/// <summary>
+	///   Simple main loop implementation that can be used to monitor
+	///   file descriptor, run timers and idle handlers.
+	/// </summary>
+	public class MainLoop {
+		/// <summary>
+		///   Condition on which to wake up from file descriptor activity
+		/// </summary>
+		[Flags]
+		public enum Condition {
+			/// <summary>
+			/// There is data to read
+			/// </summary>
+			PollIn = 1,
+			/// <summary>
+			/// Writing to the specified descriptor will not block
+			/// </summary>
+			PollOut = 2,
+			/// <summary>
+			/// There is urgent data to read
+			/// </summary>
+			PollPri = 4,
+			/// <summary>
+			///  Error condition on output
+			/// </summary>
+			PollErr = 8,
+			/// <summary>
+			/// Hang-up on output
+			/// </summary>
+			PollHup = 16,
+			/// <summary>
+			/// File descriptor is not open.
+			/// </summary>
+			PollNval = 32
+		}
+
+		class Watch {
+			public int File;
+			public Condition Condition;
+			public Func<MainLoop,bool> Callback;
+		}
+
+		class Timeout {
+			public TimeSpan Span;
+			public Func<MainLoop,bool> Callback;
+		}
+
+		Dictionary <int, Watch> descriptorWatchers = new Dictionary<int,Watch>();
+		SortedList <double, Timeout> timeouts = new SortedList<double,Timeout> ();
+		List<Func<bool>> idleHandlers = new List<Func<bool>> ();
+		
+		Pollfd [] pollmap;
+		bool poll_dirty = true;
+		int [] wakeupPipes = new int [2];
+		static IntPtr ignore = Marshal.AllocHGlobal (1);
+		
+		/// <summary>
+		///  Default constructor
+		/// </summary>
+		public MainLoop ()
+		{
+			Syscall.pipe (wakeupPipes);
+			AddWatch (wakeupPipes [0], Condition.PollIn, ml => {
+				Syscall.read (wakeupPipes [0], ignore, 1);
+				return true;
+			});
+		}
+
+		void Wakeup ()
+		{
+			Syscall.write (wakeupPipes [1], ignore, 1);
+		}
+		
+		/// <summary>
+		///   Runs @action on the thread that is processing events
+		/// </summary>
+		public void Invoke (Action action)
+		{
+			AddIdle (()=> {
+				action ();
+				return false;
+			});
+			Wakeup ();
+		}
+
+		/// <summary>
+		///   Executes the specified @idleHandler on the idle loop.  The return value is a token to remove it.
+		/// </summary>
+		public Func<bool> AddIdle (Func<bool> idleHandler)
+		{
+			lock (idleHandlers)
+				idleHandlers.Add (idleHandler);
+			return idleHandler;
+		}
+
+		/// <summary>
+		///   Removes the specified idleHandler from processing.
+		/// </summary>
+		public void RemoveIdle (Func<bool> idleHandler)
+		{
+			lock (idleHandler)
+				idleHandlers.Remove (idleHandler);
+		}
+		
+		/// <summary>
+		///  Watches a file descriptor for activity.
+		/// </summary>
+		/// <remarks>
+		///  When the condition is met, the provided callback
+		///  is invoked.  If the callback returns false, the
+		///  watch is automatically removed.
+		///
+		///  The return value is a token that represents this watch, you can
+		///  use this token to remove the watch by calling RemoveWatch.
+		/// </remarks>
+		public object AddWatch (int fileDescriptor, Condition condition, Func<MainLoop,bool> callback)
+		{
+			if (callback == null)
+				throw new ArgumentNullException ("callback");
+
+			var watch = new Watch () { Condition = condition, Callback = callback, File = fileDescriptor };
+			descriptorWatchers [fileDescriptor] = watch;
+			poll_dirty = true;
+			return watch;
+		}
+
+		/// <summary>
+		///   Removes an active watch from the mainloop.
+		/// </summary>
+		/// <remarks>
+		///   The token parameter is the value returned from AddWatch
+		/// </remarks>
+		public void RemoveWatch (object token)
+		{
+			var watch = token as Watch;
+			if (watch == null)
+				return;
+			descriptorWatchers.Remove (watch.File);
+		}
+		
+		void AddTimeout (TimeSpan time, Timeout timeout)
+		{
+			timeouts.Add ((DateTime.UtcNow + time).Ticks, timeout);
+		}
+		
+		/// <summary>
+		///   Adds a timeout to the mainloop.
+		/// </summary>
+		/// <remarks>
+		///   When time time specified passes, the callback will be invoked.
+		///   If the callback returns true, the timeout will be reset, repeating
+		///   the invocation. If it returns false, the timeout will stop.
+		///
+		///   The returned value is a token that can be used to stop the timeout
+		///   by calling RemoveTimeout.
+		/// </remarks>
+		public object AddTimeout (TimeSpan time, Func<MainLoop,bool> callback)
+		{
+			if (callback == null)
+				throw new ArgumentNullException ("callback");
+			var timeout = new Timeout () {
+				Span = time,
+				Callback = callback
+			};
+			AddTimeout (time, timeout);
+			return timeout;
+		}
+
+		/// <summary>
+		///   Removes a previously scheduled timeout
+		/// </summary>
+		/// <remarks>
+		///   The token parameter is the value returned by AddTimeout.
+		/// </remarks>
+		public void RemoveTimeout (object token)
+		{
+			var idx = timeouts.IndexOfValue (token as Timeout);
+			if (idx == -1)
+				return;
+			timeouts.RemoveAt (idx);
+		}
+
+		static PollEvents MapCondition (Condition condition)
+		{
+			PollEvents ret = 0;
+			if ((condition & Condition.PollIn) != 0)
+				ret |= PollEvents.POLLIN;
+			if ((condition & Condition.PollOut) != 0)
+				ret |= PollEvents.POLLOUT;
+			if ((condition & Condition.PollPri) != 0)
+				ret |= PollEvents.POLLPRI;
+			if ((condition & Condition.PollErr) != 0)
+				ret |= PollEvents.POLLERR;
+			if ((condition & Condition.PollHup) != 0)
+				ret |= PollEvents.POLLHUP;
+			if ((condition & Condition.PollNval) != 0)
+				ret |= PollEvents.POLLNVAL;
+			return ret;
+		}
+		
+		void UpdatePollMap ()
+		{
+			if (!poll_dirty)
+				return;
+			poll_dirty = false;
+
+			pollmap = new Pollfd [descriptorWatchers.Count];
+			int i = 0;
+			foreach (var fd in descriptorWatchers.Keys){
+				pollmap [i].fd = fd;
+				pollmap [i].events = MapCondition (descriptorWatchers [fd].Condition);
+			}
+		}
+
+		void RunTimers ()
+		{
+			long now = DateTime.UtcNow.Ticks;
+			var copy = timeouts;
+			timeouts = new SortedList<double,Timeout> ();
+			foreach (var k in copy.Keys){
+				if (k >= now)
+					break;
+
+				var timeout = copy [k];
+				if (timeout.Callback (this))
+					AddTimeout (timeout.Span, timeout);
+			}
+		}
+
+		void RunIdle ()
+		{
+			List<Func<bool>> iterate;
+			lock (idleHandlers){
+				iterate = idleHandlers;
+				idleHandlers = new List<Func<bool>> ();
+			}
+
+			foreach (var idle in iterate){
+				if (idle ())
+					lock (idleHandlers)
+						idleHandlers.Add (idle);
+			}
+		}
+		
+		bool running;
+		
+		/// <summary>
+		///   Stops the mainloop.
+		/// </summary>
+		public void Stop ()
+		{
+			running = false;
+			Wakeup ();
+		}
+
+		/// <summary>
+		///   Determines whether there are pending events to be processed.
+		/// </summary>
+		/// <remarks>
+		///   You can use this method if you want to probe if events are pending.
+		///   Typically used if you need to flush the input queue while still
+		///   running some of your own code in your main thread. 
+		/// </remarks>
+		public bool EventsPending (bool wait = false)
+		{
+			long now = DateTime.UtcNow.Ticks;
+			int pollTimeout, n;
+			if (timeouts.Count > 0)
+				pollTimeout = (int) ((timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
+			else
+				pollTimeout = -1;
+			
+			if (!wait)
+				pollTimeout = 0;
+			
+			UpdatePollMap ();
+
+			n = Syscall.poll (pollmap, (uint) pollmap.Length, pollTimeout);
+			int ic;
+			lock (idleHandlers)
+				ic = idleHandlers.Count;
+			return n > 0 || timeouts.Count > 0 && ((timeouts.Keys [0] - DateTime.UtcNow.Ticks) < 0) || ic > 0;
+		}
+
+		/// <summary>
+		///   Runs one iteration of timers and file watches
+		/// </summary>
+		/// <remarks>
+		///   You use this to process all pending events (timers, idle handlers and file watches).
+		///
+		///   You can use it like this:
+		///     while (main.EvensPending ()) MainIteration ();
+		/// </remarks>
+		public void MainIteration ()
+		{
+			if (timeouts.Count > 0)
+				RunTimers ();
+			
+			foreach (var p in pollmap){
+				Watch watch;
+
+				if (p.revents == 0)
+					continue;
+
+				if (!descriptorWatchers.TryGetValue (p.fd, out watch))
+					continue;
+				if (!watch.Callback (this))
+					descriptorWatchers.Remove (p.fd);
+			}
+			if (idleHandlers.Count > 0)
+				RunIdle ();
+		}
+		
+		/// <summary>
+		///   Runs the mainloop.
+		/// </summary>
+		public void Run ()
+		{
+			bool prev = running;
+			running = true;
+			while (running){
+				EventsPending (true);
+				MainIteration ();
+			}
+			running = prev;
+		}
+	}
+}

+ 61 - 0
Terminal.Gui/Terminal.Gui.csproj

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>Terminal.Gui</RootNamespace>
+    <AssemblyName>Terminal.Gui</AssemblyName>
+    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="Mono.Posix" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Properties\" />
+    <Folder Include="Types\" />
+    <Folder Include="Views\" />
+    <Folder Include="MonoCurses\" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Types\Point.cs" />
+    <Compile Include="Types\Rect.cs" />
+    <Compile Include="Types\Size.cs" />
+    <Compile Include="Views\Button.cs" />
+    <Compile Include="Views\Checkbox.cs" />
+    <Compile Include="Views\Dialog.cs" />
+    <Compile Include="Views\Label.cs" />
+    <Compile Include="Views\Menu.cs" />
+    <Compile Include="Views\MessageBox.cs" />
+    <Compile Include="Views\RadioGroup.cs" />
+    <Compile Include="Views\ScrollView.cs" />
+    <Compile Include="Views\TextField.cs" />
+    <Compile Include="Core.cs" />
+    <Compile Include="Driver.cs" />
+    <Compile Include="Event.cs" />
+    <Compile Include="MonoCurses\binding.cs" />
+    <Compile Include="MonoCurses\constants.cs" />
+    <Compile Include="MonoCurses\handles.cs" />
+    <Compile Include="MonoCurses\mainloop.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+</Project>

+ 0 - 0
Types/Point.cs → Terminal.Gui/Types/Point.cs


+ 0 - 0
Types/Rect.cs → Terminal.Gui/Types/Rect.cs


+ 0 - 0
Types/Size.cs → Terminal.Gui/Types/Size.cs


+ 0 - 0
Views/Button.cs → Terminal.Gui/Views/Button.cs


+ 0 - 0
Views/Checkbox.cs → Terminal.Gui/Views/Checkbox.cs


+ 0 - 0
Views/Dialog.cs → Terminal.Gui/Views/Dialog.cs


+ 0 - 0
Views/Label.cs → Terminal.Gui/Views/Label.cs


+ 0 - 0
Views/Menu.cs → Terminal.Gui/Views/Menu.cs


+ 0 - 0
Views/MessageBox.cs → Terminal.Gui/Views/MessageBox.cs


+ 0 - 0
Views/RadioGroup.cs → Terminal.Gui/Views/RadioGroup.cs


+ 0 - 0
Views/ScrollView.cs → Terminal.Gui/Views/ScrollView.cs


+ 0 - 0
Views/TextField.cs → Terminal.Gui/Views/TextField.cs


+ 4 - 21
Terminal.csproj

@@ -33,30 +33,13 @@
     <Reference Include="System" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="Core.cs" />
-    <Compile Include="Driver.cs" />
-    <Compile Include="Event.cs" />
-    <Compile Include="Types\Point.cs" />
-    <Compile Include="Types\Rect.cs" />
-    <Compile Include="Types\Size.cs" />
     <Compile Include="demo.cs" />
-    <Compile Include="Views\Label.cs" />
-    <Compile Include="Views\TextField.cs" />
-    <Compile Include="Views\Button.cs" />
-    <Compile Include="Views\Checkbox.cs" />
-    <Compile Include="Views\Menu.cs" />
-    <Compile Include="Views\ScrollView.cs" />
-    <Compile Include="Views\Dialog.cs" />
-    <Compile Include="Views\RadioGroup.cs" />
-    <Compile Include="Views\MessageBox.cs" />
   </ItemGroup>
   <ItemGroup>
-   <Reference Include="mono-curses.dll">
-     <HintPath>$(MSBuildProjectDirectory)/../mono-curses/mono-curses.dll</HintPath>
-   </Reference>
-  </ItemGroup>
-  <ItemGroup>
-    <Folder Include="Views\" />
+    <ProjectReference Include="Terminal.Gui\Terminal.Gui.csproj">
+      <Project>{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}</Project>
+      <Name>Terminal.Gui</Name>
+    </ProjectReference>
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 </Project>

+ 6 - 0
Terminal.sln

@@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 2012
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Terminal", "Terminal.csproj", "{B0A602CD-E176-449D-8663-64238D54F857}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Terminal.Gui", "Terminal.Gui\Terminal.Gui.csproj", "{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x86 = Debug|x86
@@ -13,6 +15,10 @@ Global
 		{B0A602CD-E176-449D-8663-64238D54F857}.Debug|x86.Build.0 = Debug|x86
 		{B0A602CD-E176-449D-8663-64238D54F857}.Release|x86.ActiveCfg = Release|x86
 		{B0A602CD-E176-449D-8663-64238D54F857}.Release|x86.Build.0 = Release|x86
+		{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Debug|x86.Build.0 = Debug|Any CPU
+		{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|x86.ActiveCfg = Release|Any CPU
+		{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(MonoDevelopProperties) = preSolution
 		Policies = $0

+ 9 - 9
demo.cs

@@ -13,15 +13,15 @@ class Demo {
 	static void ShowEntries (View container)
 	{
 		container.Add (
-			new Label (3, 2, "Login: "),
-			new TextField (14, 2, 40, ""),
-			new Label (3, 4, "Password: "),
-			new TextField (14, 4, 40, "") { Secret = true },
-			new CheckBox (3, 6, "Remember me"),
-			new RadioGroup (3, 8, new [] { "_Personal", "_Company" }),
-			new Button (3, 14, "Ok"),
-			new Button (10, 14, "Cancel"),
-			new Label (3, 18, "Press ESC and 9 to activate the menubar")
+			new Label (3, 6, "Login: "),
+			new TextField (14, 6, 40, ""),
+			new Label (3, 8, "Password: "),
+			new TextField (14, 8, 40, "") { Secret = true },
+			new CheckBox (3, 10, "Remember me"),
+			new RadioGroup (3, 12, new [] { "_Personal", "_Company" }),
+			new Button (3, 18, "Ok"),
+			new Button (10, 18, "Cancel"),
+			new Label (3, 22, "Press ESC and 9 to activate the menubar")
 		);
 	}