Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

Console.d

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003         @file Console.d
00004         
00005         Copyright (c) 2004 Kris Bell
00006         
00007         This software is provided 'as-is', without any express or implied
00008         warranty. In no event will the authors be held liable for damages
00009         of any kind arising from the use of this software.
00010         
00011         Permission is hereby granted to anyone to use this software for any 
00012         purpose, including commercial applications, and to alter it and/or 
00013         redistribute it freely, subject to the following restrictions:
00014         
00015         1. The origin of this software must not be misrepresented; you must 
00016            not claim that you wrote the original software. If you use this 
00017            software in a product, an acknowledgment within documentation of 
00018            said product would be appreciated but is not required.
00019 
00020         2. Altered source versions must be plainly marked as such, and must 
00021            not be misrepresented as being the original software.
00022 
00023         3. This notice may not be removed or altered from any distribution
00024            of the source.
00025 
00026         4. Derivative works are permitted, but they must carry this notice
00027            in full and credit the original source.
00028 
00029 
00030                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00031 
00032        
00033         @version        Initial version; Feb 2005      
00034         @version        Heavily revised for unicode; November 2005
00035 
00036         @author         Kris
00037 
00038 
00039 *******************************************************************************/
00040 
00041 module mango.io.Console;
00042 
00043 private import  mango.sys.OS;
00044 
00045 private import  mango.io.Buffer,
00046                 mango.io.DeviceConduit;
00047 
00048 
00049 /*******************************************************************************
00050 
00051         Bring in native Windows functions
00052 
00053 *******************************************************************************/
00054 
00055 version (Win32)
00056 {
00057         private extern (Windows) 
00058         {
00059                 HANDLE GetStdHandle   (DWORD);
00060                 DWORD  GetConsoleMode (HANDLE, LPDWORD);
00061                 BOOL   ReadConsoleW   (HANDLE, VOID*, DWORD, LPDWORD, LPVOID);
00062                 BOOL   WriteConsoleW  (HANDLE, VOID*, DWORD, LPDWORD, LPVOID);
00063 
00064                 const uint CP_UTF8 = 65001;
00065                 int    WideCharToMultiByte(UINT, DWORD, wchar*, int, void*, int, LPCSTR, LPBOOL);
00066                 int    MultiByteToWideChar(UINT, DWORD, void*, int,  wchar*, int);
00067         }
00068 }
00069 
00070 /*******************************************************************************
00071 
00072         low level console IO support. 
00073         
00074         Note that for a while this was templated for each of char, wchar, 
00075         and dchar. It became clear after some usage that the console is
00076         more useful if it sticks to Utf8 only. See ConsoleConduit below
00077         for details.
00078 
00079         Redirecting the standard IO handles (via a shell) operates as one 
00080         would expect.
00081 
00082 *******************************************************************************/
00083 
00084 struct Console 
00085 {
00086         /**********************************************************************
00087 
00088                 Model console input as a buffer
00089 
00090         **********************************************************************/
00091 
00092         class Input  : Buffer
00093         {
00094                 alias getConduit conduit;
00095 
00096                 this (FileDevice device)
00097                 {
00098                         super (new ConsoleConduit(device));
00099                 }
00100 
00101                 Input opCall (inout char[] x)
00102                 {
00103                         if (readable == 0)
00104                             fill ();
00105 
00106                         x = cast(char[]) get (readable);
00107                         return this;
00108                 }
00109         }
00110 
00111         /**********************************************************************
00112 
00113                 Model console output as a buffer
00114 
00115         **********************************************************************/
00116 
00117         class Output : Buffer
00118         {
00119                 alias getConduit conduit;
00120 
00121                 this (FileDevice device)
00122                 {
00123                         super (new ConsoleConduit(device));
00124                 }
00125 
00126                 Output opCall (char[] x)
00127                 {
00128                         append(x).flush();
00129                         return this;
00130                 }           
00131         }
00132 
00133         /***********************************************************************
00134 
00135                 Conduit for specifically handling the console devices. This 
00136                 takes care of certain implementation details on the Win32 
00137                 platform.
00138 
00139                 Note that the console is fixed at Utf8 for both linux and
00140                 Win32. The latter is actually Utf16 native, but it's just
00141                 too much hassle for a developer to handle the distinction
00142                 when it really should be a no-brainer. In particular, the
00143                 Win32 console functions don't work with redirection. This
00144                 causes additional difficulties that can be ameliorated by
00145                 asserting console I/O is always Utf8, in all modes.
00146 
00147         ***********************************************************************/
00148 
00149         class ConsoleConduit : DeviceConduit
00150         {
00151                 /***************************************************************
00152 
00153                         Windows-specific code
00154 
00155                 ***************************************************************/
00156 
00157                 version (Win32)
00158                         {
00159                         private wchar[] input;
00160                         private wchar[] output;
00161 
00162                         private bool redirect = false;
00163 
00164                         /*******************************************************
00165 
00166                                 Create a FileConduit on the provided 
00167                                 FileDevice. 
00168 
00169                                 This is strictly for adapting existing 
00170                                 devices such as Stdout and friends
00171 
00172                         *******************************************************/
00173 
00174                         private this (FileDevice device)
00175                         {
00176                                 super (device);
00177                                 input = new wchar [1024 * 1];
00178                                 output = new wchar [1024 * 1];
00179                         }    
00180 
00181                         /*******************************************************
00182         
00183                                 Return a preferred size for buffering 
00184                                 console I/O. This must be less than 32KB 
00185                                 for Win32!
00186 
00187                         *******************************************************/
00188 
00189                         uint bufferSize ()
00190                         {
00191                                 return 1024 * 8;
00192                         }
00193 
00194                         /*******************************************************
00195 
00196                                 Gain access to the standard IO handles 
00197 
00198                         *******************************************************/
00199 
00200                         protected override void reopen (FileDevice device)
00201                         {
00202                                 static const DWORD[] id = [
00203                                                           cast(DWORD) -10, 
00204                                                           cast(DWORD) -11, 
00205                                                           cast(DWORD) -12
00206                                                           ];
00207                                 static const char[][] f = [
00208                                                           "CONIN$\0", 
00209                                                           "CONOUT$\0", 
00210                                                           "CONOUT$\0"
00211                                                           ];
00212 
00213                                 assert (device.id < 3);
00214                                 handle = GetStdHandle (id[device.id]);
00215                                 if (! handle)
00216                                       handle = CreateFileA (f[device.id], 
00217                                                GENERIC_READ | GENERIC_WRITE,  
00218                                                FILE_SHARE_READ | FILE_SHARE_WRITE, 
00219                                                null, OPEN_EXISTING, 0, null);
00220                                 if (! handle)
00221                                       error ();
00222 
00223                                 // are we redirecting?
00224                                 DWORD mode;
00225                                 if (! GetConsoleMode (handle, &mode))
00226                                       redirect = true;
00227                         }
00228 
00229                         /*******************************************************
00230 
00231                                 Write a chunk of bytes to the console from the 
00232                                 provided array (typically that belonging to 
00233                                 an IBuffer)
00234 
00235                         *******************************************************/
00236 
00237                         version (Win32SansUnicode) 
00238                                 {} 
00239                              else
00240                                 {
00241                                 protected override uint writer (void[] src)
00242                                 {
00243                                 if (redirect)
00244                                     return super.writer (src);
00245                                 else
00246                                    {
00247                                    DWORD i = src.length;
00248 
00249                                    // protect convertion from empty strings
00250                                    if (i is 0)
00251                                        return 0;
00252 
00253                                    // expand buffer appropriately
00254                                    if (output.length < i)
00255                                        output.length = i;
00256 
00257                                    // convert into output buffer
00258                                    i = MultiByteToWideChar (CP_UTF8, 0, src.ptr, i, 
00259                                                             output.ptr, output.length);
00260                                             
00261                                    // flush produced output
00262                                    for (wchar* p=output.ptr, end=output.ptr+i; p < end; p+=i)
00263                                         if (! WriteConsoleW (handle, p, end - p, &i, null))
00264                                               error();
00265                                    return src.length;
00266                                    }
00267                                 }
00268                                 }
00269                         
00270                         /*******************************************************
00271 
00272                                 Read a chunk of bytes from the console into the 
00273                                 provided array (typically that belonging to 
00274                                 an IBuffer)
00275 
00276                         *******************************************************/
00277 
00278                         version (Win32SansUnicode) 
00279                                 {} 
00280                              else
00281                                 {
00282                                 protected override uint reader (void[] dst)
00283                                 {
00284                                 if (redirect)
00285                                     return super.reader (dst);
00286                                 else
00287                                    {
00288                                    DWORD i = dst.length / 4;
00289 
00290                                    assert (i);
00291 
00292                                    if (i > input.length)
00293                                        i = input.length;
00294                                        
00295                                    // read a chunk of wchars from the console
00296                                    if (! ReadConsoleW (handle, input.ptr, i, &i, null))
00297                                          error();
00298 
00299                                    // no input ~ go home
00300                                    if (i is 0)
00301                                        return Eof;
00302 
00303                                    // translate to utf8, directly into dst
00304                                    i = WideCharToMultiByte (CP_UTF8, 0, input.ptr, i, 
00305                                                             dst.ptr, dst.length, null, null);
00306                                    if (i is 0)
00307                                        error ();
00308 
00309                                    return i;
00310                                    }
00311                                 }
00312                                 }
00313 
00314                         }
00315                      else
00316                         {
00317                         /*******************************************************
00318 
00319                                 Create a FileConduit on the provided 
00320                                 FileDevice. 
00321 
00322                                 This is strictly for adapting existing 
00323                                 devices such as Stdout and friends
00324 
00325                         *******************************************************/
00326 
00327                         private this (FileDevice device)
00328                         {
00329                                 super (device);
00330                         }
00331                         }
00332         }
00333 }
00334 
00335 
00336 /******************************************************************************
00337 
00338 ******************************************************************************/
00339 
00340 // public console instances 
00341 static Console.Input    Cin;
00342 static Console.Output   Cout, 
00343                         Cerr;
00344 
00345 static this ()
00346 {
00347         Cin  = new Console.Input  (new FileDevice (0, ConduitStyle.ReadText));
00348         Cout = new Console.Output (new FileDevice (1, ConduitStyle.WriteText));
00349         Cerr = new Console.Output (new FileDevice (2, ConduitStyle.WriteText));
00350 }

Generated on Sat Dec 24 17:28:32 2005 for Mango by  doxygen 1.4.0