From 8c65293e7fabd67b2993164dac0132232b3ea2db Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 3 May 2021 21:26:01 +0100 Subject: [PATCH] Fixxes #1272. Using ioctl to get winsize, resizeterm to resize and added the WinChanged action. --- .../CursesDriver/CursesDriver.cs | 20 ++-- .../CursesDriver/UnixMainLoop.cs | 27 +++-- .../ConsoleDrivers/CursesDriver/binding.cs | 103 +++++++++++++----- .../ConsoleDrivers/CursesDriver/constants.cs | 4 +- 4 files changed, 112 insertions(+), 42 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 2874ffaf4e..c8c4a27204 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -74,10 +74,10 @@ public override void AddStr (ustring str) public override void Refresh () { Curses.refresh (); - if (Curses.CheckWinChange ()) { - Clip = new Rect (0, 0, Cols, Rows); - TerminalResized?.Invoke (); - } + //if (Curses.CheckWinChange ()) { + // Clip = new Rect (0, 0, Cols, Rows); + // TerminalResized?.Invoke (); + //} } public override void UpdateCursor () => Refresh (); @@ -97,13 +97,13 @@ public override void End () //Console.Out.Write ("\x1b[2J"); //Console.Out.Flush (); - // Reports current cursor line and column. + // Set top and bottom lines of a window. //Console.Out.Write ("\x1b[1;25r"); //Console.Out.Flush (); //Set cursor key to cursor. - Console.Out.Write("\x1b[?1l"); - Console.Out.Flush(); + Console.Out.Write ("\x1b[?1l"); + Console.Out.Flush (); } public override void UpdateScreen () => window.redrawwin (); @@ -695,6 +695,12 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle return true; }); + mLoop.WinChanged += () => { + if (Curses.CheckWinChange ()) { + Clip = new Rect (0, 0, Cols, Rows); + TerminalResized?.Invoke (); + } + }; } Curses.Event oldMouseEvents, reportableMouseEvents; diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs index d6fc9d9873..feccf6126b 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs @@ -38,6 +38,8 @@ namespace Terminal.Gui { /// can watch file descriptors using the AddWatch methods. /// internal class UnixMainLoop : IMainLoopDriver { + public const int KEY_RESIZE = unchecked((int)0xffffffffffffffff); + [StructLayout (LayoutKind.Sequential)] struct Pollfd { public int fd; @@ -84,10 +86,10 @@ class Watch { Dictionary descriptorWatchers = new Dictionary (); [DllImport ("libc")] - extern static int poll ([In, Out]Pollfd [] ufds, uint nfds, int timeout); + extern static int poll ([In, Out] Pollfd [] ufds, uint nfds, int timeout); [DllImport ("libc")] - extern static int pipe ([In, Out]int [] pipes); + extern static int pipe ([In, Out] int [] pipes); [DllImport ("libc")] extern static int read (int fd, IntPtr buf, IntPtr n); @@ -100,13 +102,17 @@ class Watch { int [] wakeupPipes = new int [2]; static IntPtr ignore = Marshal.AllocHGlobal (1); MainLoop mainLoop; + bool winChanged; + + public Action WinChanged; void IMainLoopDriver.Wakeup () { - write (wakeupPipes [1], ignore, (IntPtr) 1); + write (wakeupPipes [1], ignore, (IntPtr)1); } - void IMainLoopDriver.Setup (MainLoop mainLoop) { + void IMainLoopDriver.Setup (MainLoop mainLoop) + { this.mainLoop = mainLoop; pipe (wakeupPipes); AddWatch (wakeupPipes [0], Condition.PollIn, ml => { @@ -143,7 +149,7 @@ public void RemoveWatch (object token) public object AddWatch (int fileDescriptor, Condition condition, Func callback) { if (callback == null) - throw new ArgumentNullException (nameof(callback)); + throw new ArgumentNullException (nameof (callback)); var watch = new Watch () { Condition = condition, Callback = callback, File = fileDescriptor }; descriptorWatchers [fileDescriptor] = watch; @@ -175,8 +181,11 @@ bool IMainLoopDriver.EventsPending (bool wait) UpdatePollMap (); var n = poll (pollmap, (uint)pollmap.Length, pollTimeout); - - return n > 0 || CheckTimers (wait, out pollTimeout); + + if (n == KEY_RESIZE) { + winChanged = true; + } + return n >= KEY_RESIZE || CheckTimers (wait, out pollTimeout); } bool CheckTimers (bool wait, out int pollTimeout) @@ -204,6 +213,10 @@ bool CheckTimers (bool wait, out int pollTimeout) void IMainLoopDriver.MainIteration () { + if (winChanged) { + winChanged = false; + WinChanged?.Invoke (); + } if (pollmap != null) { foreach (var p in pollmap) { Watch watch; diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs index 857357908d..add2342f67 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs @@ -48,6 +48,14 @@ namespace Unix.Terminal { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member public partial class Curses { + [StructLayout (LayoutKind.Sequential)] + public struct winsize { + public ushort ws_row; + public ushort ws_col; + public ushort ws_xpixel; /* unused */ + public ushort ws_ypixel; /* unused */ + }; + [StructLayout (LayoutKind.Sequential)] public struct MouseEvent { public short ID; @@ -66,8 +74,11 @@ public struct MouseEvent { static NativeMethods methods; - [DllImport("libc")] - public extern static int setlocale(int cate, [MarshalAs(UnmanagedType.LPStr)] string locale); + [DllImport ("libc")] + public extern static int setlocale (int cate, [MarshalAs (UnmanagedType.LPStr)] string locale); + + [DllImport ("libc")] + public extern static int ioctl (int fd, int cmd, out winsize argp); static void LoadMethods () { @@ -86,18 +97,18 @@ static void FindNCurses () lines_ptr = get_ptr ("LINES"); cols_ptr = get_ptr ("COLS"); } - + static public Window initscr () { - setlocale(LC_ALL, ""); + setlocale (LC_ALL, ""); FindNCurses (); - + main_window = new Window (methods.initscr ()); try { console_sharp_get_dims (out lines, out cols); - } catch (DllNotFoundException){ + } catch (DllNotFoundException) { endwin (); - Console.Error.WriteLine ("Unable to find the @MONO_CURSES@ native library\n" + + 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"); @@ -106,7 +117,7 @@ static public Window initscr () return main_window; } - public static int Lines { + public static int Lines { get { return lines; } @@ -125,16 +136,22 @@ public static int Cols { public static bool CheckWinChange () { int l, c; - + console_sharp_get_dims (out l, out c); - if (l != lines || c != cols){ + + if (l == 1 || l != lines || c != cols) { lines = l; cols = c; + if (l <= 0 || c <= 0) { + Console.Out.Write ($"\x1b[8;50;{c}t"); + Console.Out.Flush (); + return false; + } return true; } return false; } - + public static int addstr (string format, params object [] args) { var s = string.Format (format, args); @@ -151,9 +168,9 @@ public static int addstr (string format, params object [] args) // public static int addch (int ch) { - if (ch < 127 || ch > 0xffff ) + if (ch < 127 || ch > 0xffff) return methods.addch (ch); - char c = (char) ch; + char c = (char)ch; return addwstr (new String (c, 1)); } @@ -167,7 +184,7 @@ static IntPtr get_ptr (string key) throw new Exception ("Could not load the key " + key); return ptr; } - + internal static IntPtr read_static_ptr (string key) { var ptr = get_ptr (key); @@ -175,8 +192,7 @@ internal static IntPtr read_static_ptr (string key) } internal static IntPtr console_sharp_get_stdscr () => stdscr; - - + internal static IntPtr console_sharp_get_curscr () { return Marshal.ReadIntPtr (curscr_ptr); @@ -184,15 +200,36 @@ internal static IntPtr console_sharp_get_curscr () internal static void console_sharp_get_dims (out int lines, out int cols) { - lines = Marshal.ReadInt32 (lines_ptr); - cols = Marshal.ReadInt32 (cols_ptr); + //lines = Marshal.ReadInt32 (lines_ptr); + //cols = Marshal.ReadInt32 (cols_ptr); + + int cmd; + if (UnmanagedLibrary.IsMacOSPlatform) { + cmd = TIOCGWINSZ_MAC; + } else { + cmd = TIOCGWINSZ; + } + + if (ioctl (1, cmd, out winsize ws) == 0) { + lines = ws.ws_row; + cols = ws.ws_col; + + if (lines == Lines && cols == Cols) { + return; + } + + resizeterm (lines, cols); + } else { + lines = Lines; + cols = Cols; + } } public static Event mousemask (Event newmask, out Event oldmask) { IntPtr e; - var ret = (Event) (methods.mousemask ((IntPtr) newmask, out e)); - oldmask = (Event) e; + var ret = (Event)(methods.mousemask ((IntPtr)newmask, out e)); + oldmask = (Event)e; return ret; } @@ -210,7 +247,7 @@ static public int IsAlt (int key) public static bool HasColors => methods.has_colors (); public static int InitColorPair (short pair, short foreground, short background) => methods.init_pair (pair, foreground, background); public static int UseDefaultColors () => methods.use_default_colors (); - public static int ColorPairs => methods.COLOR_PAIRS(); + public static int ColorPairs => methods.COLOR_PAIRS (); // // The proxy methods to call into each version @@ -240,11 +277,11 @@ static public int IsAlt (int key) static public int leaveok (IntPtr win, bool bf) => methods.leaveok (win, bf); static public int wsetscrreg (IntPtr win, int top, int bot) => methods.wsetscrreg (win, top, bot); static public int scrollok (IntPtr win, bool bf) => methods.scrollok (win, bf); - static public int nl() => methods.nl(); - static public int nonl() => methods.nonl(); + static public int nl () => methods.nl (); + static public int nonl () => methods.nonl (); static public int setscrreg (int top, int bot) => methods.setscrreg (top, bot); static public int refresh () => methods.refresh (); - static public int doupdate() => methods.doupdate(); + static public int doupdate () => methods.doupdate (); static public int wrefresh (IntPtr win) => methods.wrefresh (win); static public int redrawwin (IntPtr win) => methods.redrawwin (win); //static public int wredrawwin (IntPtr win, int beg_line, int num_lines) => methods.wredrawwin (win, beg_line, num_lines); @@ -266,10 +303,13 @@ static public int IsAlt (int key) static public int start_color () => methods.start_color (); static public int init_pair (short pair, short f, short b) => methods.init_pair (pair, f, b); static public int use_default_colors () => methods.use_default_colors (); - static public int COLOR_PAIRS() => methods.COLOR_PAIRS(); + static public int COLOR_PAIRS () => methods.COLOR_PAIRS (); static public uint getmouse (out MouseEvent ev) => methods.getmouse (out ev); static public uint ungetmouse (ref MouseEvent ev) => methods.ungetmouse (ref ev); static public int mouseinterval (int interval) => methods.mouseinterval (interval); + static public bool is_term_resized (int lines, int columns) => methods.is_term_resized (lines, columns); + static public int resize_term (int lines, int columns) => methods.resize_term (lines, columns); + static public int resizeterm (int lines, int columns) => methods.resizeterm (lines, columns); } #pragma warning disable RCS1102 // Make class static. @@ -313,7 +353,7 @@ internal class Delegates { public delegate int move (int line, int col); public delegate int curs_set (int visibility); public delegate int addch (int ch); - public delegate int addwstr([MarshalAs(UnmanagedType.LPWStr)]string s); + public delegate int addwstr ([MarshalAs (UnmanagedType.LPWStr)] string s); public delegate int wmove (IntPtr win, int line, int col); public delegate int waddch (IntPtr win, int ch); public delegate int attron (int attrs); @@ -332,6 +372,9 @@ internal class Delegates { public delegate uint ungetmouse (ref Curses.MouseEvent ev); public delegate int mouseinterval (int interval); public delegate IntPtr mousemask (IntPtr newmask, out IntPtr oldMask); + public delegate bool is_term_resized (int lines, int columns); + public delegate int resize_term (int lines, int columns); + public delegate int resizeterm (int lines, int columns); } internal class NativeMethods { @@ -392,6 +435,9 @@ internal class NativeMethods { public readonly Delegates.ungetmouse ungetmouse; public readonly Delegates.mouseinterval mouseinterval; public readonly Delegates.mousemask mousemask; + public readonly Delegates.is_term_resized is_term_resized; + public readonly Delegates.resize_term resize_term; + public readonly Delegates.resizeterm resizeterm; public UnmanagedLibrary UnmanagedLibrary; public NativeMethods (UnmanagedLibrary lib) @@ -434,7 +480,7 @@ public NativeMethods (UnmanagedLibrary lib) wnoutrefresh = lib.GetNativeMethodDelegate ("wnoutrefresh"); move = lib.GetNativeMethodDelegate ("move"); curs_set = lib.GetNativeMethodDelegate ("curs_set"); - addch = lib.GetNativeMethodDelegate("addch"); + addch = lib.GetNativeMethodDelegate ("addch"); addwstr = lib.GetNativeMethodDelegate ("addwstr"); wmove = lib.GetNativeMethodDelegate ("wmove"); waddch = lib.GetNativeMethodDelegate ("waddch"); @@ -454,6 +500,9 @@ public NativeMethods (UnmanagedLibrary lib) ungetmouse = lib.GetNativeMethodDelegate ("ungetmouse"); mouseinterval = lib.GetNativeMethodDelegate ("mouseinterval"); mousemask = lib.GetNativeMethodDelegate ("mousemask"); + is_term_resized = lib.GetNativeMethodDelegate ("is_term_resized"); + resize_term = lib.GetNativeMethodDelegate ("resize_term"); + resizeterm = lib.GetNativeMethodDelegate ("resizeterm"); } } #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs index 8b964ddebc..77e9c96785 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs @@ -53,6 +53,9 @@ public partial class Curses { public const int COLOR_WHITE = unchecked((int)0x7); public const int KEY_CODE_YES = unchecked((int)0x100); public const int KEY_CODE_SEQ = unchecked((int)0x5b); + public const int ERR = unchecked((int)0xffffffff); + public const int TIOCGWINSZ = unchecked((int)0x5413); + public const int TIOCGWINSZ_MAC = unchecked((int)0x40087468); [Flags] public enum Event : long { @@ -93,7 +96,6 @@ public enum Event : long { public const int DownEnd = unchecked((int)0x0); public const int Home = unchecked((int)0x0); #endif - 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);