diff --git a/README.md b/README.md
index 028a67584..80982b08b 100644
--- a/README.md
+++ b/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
diff --git a/Core.cs b/Terminal.Gui/Core.cs
similarity index 100%
rename from Core.cs
rename to Terminal.Gui/Core.cs
diff --git a/Driver.cs b/Terminal.Gui/Driver.cs
similarity index 100%
rename from Driver.cs
rename to Terminal.Gui/Driver.cs
diff --git a/Event.cs b/Terminal.Gui/Event.cs
similarity index 100%
rename from Event.cs
rename to Terminal.Gui/Event.cs
diff --git a/Terminal.Gui/MonoCurses/README.md b/Terminal.Gui/MonoCurses/README.md
new file mode 100644
index 000000000..4ea82ddaf
--- /dev/null
+++ b/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
diff --git a/Terminal.Gui/MonoCurses/binding.cs b/Terminal.Gui/MonoCurses/binding.cs
new file mode 100644
index 000000000..e08024f1e
--- /dev/null
+++ b/Terminal.Gui/MonoCurses/binding.cs
@@ -0,0 +1,382 @@
+
+//
+// binding.cs.in: Core binding for curses.
+//
+// Authors:
+// Miguel de Icaza (miguel.de.icaza@gmail.com)
+//
+// 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;
+ }
+ }
+}
diff --git a/Terminal.Gui/MonoCurses/constants.cs b/Terminal.Gui/MonoCurses/constants.cs
new file mode 100644
index 000000000..7a3864cb6
--- /dev/null
+++ b/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;
+ }
+
+ }
+}
diff --git a/Terminal.Gui/MonoCurses/handles.cs b/Terminal.Gui/MonoCurses/handles.cs
new file mode 100644
index 000000000..e91887256
--- /dev/null
+++ b/Terminal.Gui/MonoCurses/handles.cs
@@ -0,0 +1,172 @@
+//
+// handles.cs: OO wrappers for some curses objects
+//
+// Authors:
+// Miguel de Icaza (miguel.de.icaza@gmail.com)
+//
+// 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;
+ }
+ }
+
+ }
+
+}
diff --git a/Terminal.Gui/MonoCurses/mainloop.cs b/Terminal.Gui/MonoCurses/mainloop.cs
new file mode 100644
index 000000000..442319eab
--- /dev/null
+++ b/Terminal.Gui/MonoCurses/mainloop.cs
@@ -0,0 +1,362 @@
+//
+// mainloop.cs: Simple managed mainloop implementation.
+//
+// Authors:
+// Miguel de Icaza (miguel.de.icaza@gmail.com)
+//
+// 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 {
+
+ ///
+ /// Simple main loop implementation that can be used to monitor
+ /// file descriptor, run timers and idle handlers.
+ ///
+ public class MainLoop {
+ ///
+ /// Condition on which to wake up from file descriptor activity
+ ///
+ [Flags]
+ public enum Condition {
+ ///
+ /// There is data to read
+ ///
+ PollIn = 1,
+ ///
+ /// Writing to the specified descriptor will not block
+ ///
+ PollOut = 2,
+ ///
+ /// There is urgent data to read
+ ///
+ PollPri = 4,
+ ///
+ /// Error condition on output
+ ///
+ PollErr = 8,
+ ///
+ /// Hang-up on output
+ ///
+ PollHup = 16,
+ ///
+ /// File descriptor is not open.
+ ///
+ PollNval = 32
+ }
+
+ class Watch {
+ public int File;
+ public Condition Condition;
+ public Func Callback;
+ }
+
+ class Timeout {
+ public TimeSpan Span;
+ public Func Callback;
+ }
+
+ Dictionary descriptorWatchers = new Dictionary();
+ SortedList timeouts = new SortedList ();
+ List> idleHandlers = new List> ();
+
+ Pollfd [] pollmap;
+ bool poll_dirty = true;
+ int [] wakeupPipes = new int [2];
+ static IntPtr ignore = Marshal.AllocHGlobal (1);
+
+ ///
+ /// Default constructor
+ ///
+ 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);
+ }
+
+ ///
+ /// Runs @action on the thread that is processing events
+ ///
+ public void Invoke (Action action)
+ {
+ AddIdle (()=> {
+ action ();
+ return false;
+ });
+ Wakeup ();
+ }
+
+ ///
+ /// Executes the specified @idleHandler on the idle loop. The return value is a token to remove it.
+ ///
+ public Func AddIdle (Func idleHandler)
+ {
+ lock (idleHandlers)
+ idleHandlers.Add (idleHandler);
+ return idleHandler;
+ }
+
+ ///
+ /// Removes the specified idleHandler from processing.
+ ///
+ public void RemoveIdle (Func idleHandler)
+ {
+ lock (idleHandler)
+ idleHandlers.Remove (idleHandler);
+ }
+
+ ///
+ /// Watches a file descriptor for activity.
+ ///
+ ///
+ /// 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.
+ ///
+ public object AddWatch (int fileDescriptor, Condition condition, Func 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;
+ }
+
+ ///
+ /// Removes an active watch from the mainloop.
+ ///
+ ///
+ /// The token parameter is the value returned from AddWatch
+ ///
+ 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);
+ }
+
+ ///
+ /// Adds a timeout to the mainloop.
+ ///
+ ///
+ /// 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.
+ ///
+ public object AddTimeout (TimeSpan time, Func callback)
+ {
+ if (callback == null)
+ throw new ArgumentNullException ("callback");
+ var timeout = new Timeout () {
+ Span = time,
+ Callback = callback
+ };
+ AddTimeout (time, timeout);
+ return timeout;
+ }
+
+ ///
+ /// Removes a previously scheduled timeout
+ ///
+ ///
+ /// The token parameter is the value returned by AddTimeout.
+ ///
+ 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 ();
+ 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> iterate;
+ lock (idleHandlers){
+ iterate = idleHandlers;
+ idleHandlers = new List> ();
+ }
+
+ foreach (var idle in iterate){
+ if (idle ())
+ lock (idleHandlers)
+ idleHandlers.Add (idle);
+ }
+ }
+
+ bool running;
+
+ ///
+ /// Stops the mainloop.
+ ///
+ public void Stop ()
+ {
+ running = false;
+ Wakeup ();
+ }
+
+ ///
+ /// Determines whether there are pending events to be processed.
+ ///
+ ///
+ /// 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.
+ ///
+ 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;
+ }
+
+ ///
+ /// Runs one iteration of timers and file watches
+ ///
+ ///
+ /// You use this to process all pending events (timers, idle handlers and file watches).
+ ///
+ /// You can use it like this:
+ /// while (main.EvensPending ()) MainIteration ();
+ ///
+ 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 ();
+ }
+
+ ///
+ /// Runs the mainloop.
+ ///
+ public void Run ()
+ {
+ bool prev = running;
+ running = true;
+ while (running){
+ EventsPending (true);
+ MainIteration ();
+ }
+ running = prev;
+ }
+ }
+}
diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj
new file mode 100644
index 000000000..607f5a8c1
--- /dev/null
+++ b/Terminal.Gui/Terminal.Gui.csproj
@@ -0,0 +1,61 @@
+
+
+
+ Debug
+ AnyCPU
+ {00F366F8-DEE4-482C-B9FD-6DB0200B79E5}
+ Library
+ Terminal.Gui
+ Terminal.Gui
+ v4.6.1
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+ false
+
+
+ true
+ bin\Release
+ prompt
+ 4
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Types/Point.cs b/Terminal.Gui/Types/Point.cs
similarity index 100%
rename from Types/Point.cs
rename to Terminal.Gui/Types/Point.cs
diff --git a/Types/Rect.cs b/Terminal.Gui/Types/Rect.cs
similarity index 100%
rename from Types/Rect.cs
rename to Terminal.Gui/Types/Rect.cs
diff --git a/Types/Size.cs b/Terminal.Gui/Types/Size.cs
similarity index 100%
rename from Types/Size.cs
rename to Terminal.Gui/Types/Size.cs
diff --git a/Views/Button.cs b/Terminal.Gui/Views/Button.cs
similarity index 100%
rename from Views/Button.cs
rename to Terminal.Gui/Views/Button.cs
diff --git a/Views/Checkbox.cs b/Terminal.Gui/Views/Checkbox.cs
similarity index 100%
rename from Views/Checkbox.cs
rename to Terminal.Gui/Views/Checkbox.cs
diff --git a/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs
similarity index 100%
rename from Views/Dialog.cs
rename to Terminal.Gui/Views/Dialog.cs
diff --git a/Views/Label.cs b/Terminal.Gui/Views/Label.cs
similarity index 100%
rename from Views/Label.cs
rename to Terminal.Gui/Views/Label.cs
diff --git a/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs
similarity index 100%
rename from Views/Menu.cs
rename to Terminal.Gui/Views/Menu.cs
diff --git a/Views/MessageBox.cs b/Terminal.Gui/Views/MessageBox.cs
similarity index 100%
rename from Views/MessageBox.cs
rename to Terminal.Gui/Views/MessageBox.cs
diff --git a/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs
similarity index 100%
rename from Views/RadioGroup.cs
rename to Terminal.Gui/Views/RadioGroup.cs
diff --git a/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs
similarity index 100%
rename from Views/ScrollView.cs
rename to Terminal.Gui/Views/ScrollView.cs
diff --git a/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
similarity index 100%
rename from Views/TextField.cs
rename to Terminal.Gui/Views/TextField.cs
diff --git a/Terminal.csproj b/Terminal.csproj
index 030161e8f..d5005b9cf 100644
--- a/Terminal.csproj
+++ b/Terminal.csproj
@@ -33,30 +33,13 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $(MSBuildProjectDirectory)/../mono-curses/mono-curses.dll
-
-
-
-
+
+ {00F366F8-DEE4-482C-B9FD-6DB0200B79E5}
+ Terminal.Gui
+
diff --git a/Terminal.sln b/Terminal.sln
index 98efa6dac..d08ae8d72 100644
--- a/Terminal.sln
+++ b/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
diff --git a/demo.cs b/demo.cs
index 763cb476b..88a391c82 100644
--- a/demo.cs
+++ b/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")
);
}