summaryrefslogtreecommitdiff
path: root/rhtvision/classes/winnt/winntscr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'rhtvision/classes/winnt/winntscr.cc')
-rw-r--r--rhtvision/classes/winnt/winntscr.cc577
1 files changed, 577 insertions, 0 deletions
diff --git a/rhtvision/classes/winnt/winntscr.cc b/rhtvision/classes/winnt/winntscr.cc
new file mode 100644
index 0000000..e9ebf8a
--- /dev/null
+++ b/rhtvision/classes/winnt/winntscr.cc
@@ -0,0 +1,577 @@
+/**[txh]********************************************************************
+
+ Copyright (c) 2002-2005 by Salvador E. Tropea (SET)
+ Based on code contributed by Anatoli Soltan.
+
+ Description:
+ WinNT Screen routines.
+ The original implementation was done by Anatoli, I removed some code,
+added some routines and adapted it to the new architecture.
+
+ ToDo: Set UseScreenSaver when we are in full screen.
+
+ Configuration variables:
+ ScreenWidth
+ ScreenHeight
+ AppCP
+ ScrCP
+ InpCP
+
+ Notes:
+ 1) I saw a problem in W98SE, it looks like a bug in Windows: If I
+suspend to a shell and the resume doing window size changes at exit the
+screen seems to be partially restored. But if you force windows to redraw
+it (minimize/maximize for example) things gets right. This is problem only
+affects the cursor when using USE_NEW_BUFFER.
+ 2) The USE_NEW_BUFFER mode is something I (SET) found in the Win32 API
+docs that's much cleaver than saving/restoring the screen contents. When
+defined I just create a new screen buffer and use it. To restore the screen
+you just need to set the original STDOUT handle as the active. It makes the
+code easier and exposes less Windows problems (bugs?). The only bizarre
+thing I observe is "invisible cursor", but this is just Windows forgets to
+update, as soon as the window needs a redraw the cursor gets visible again.
+ 3) Anatoli left commented:
+ hIn=CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+ hOut=CreateFile("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+I think they aren't useful but I left it here in case I need this example.
+
+***************************************************************************/
+#include <tv/configtv.h>
+
+#define Uses_stdlib
+#define Uses_TScreen
+#define Uses_TEvent
+#define Uses_TGKey
+#define Uses_TVCodePage
+#define Uses_unistd
+#include <tv.h>
+#include <tv/win32/win32clip.h>
+
+// I delay the check to generate as much dependencies as possible
+#ifdef TVOS_Win32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef YieldProcessor
+
+#include <tv/winnt/screen.h>
+#include <tv/winnt/mouse.h>
+#include <tv/winnt/key.h>
+
+//#define DEBUG
+#ifdef DEBUG
+ #define DBPr1(a) fputs(a,stderr)
+ #define DBPr2(a,b) fprintf(stderr,a,b)
+ #define DBPr3(a,b,c) fprintf(stderr,a,b,c)
+#else
+ #define DBPr1(a)
+ #define DBPr2(a,b)
+ #define DBPr3(a,b,c)
+#endif
+
+#define TV_CONSOLE_MODE (ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT)
+
+#ifndef USE_NEW_BUFFER
+ushort *TScreenWinNT::saveScreenBuf;
+unsigned TScreenWinNT::saveScreenSize;
+unsigned TScreenWinNT::saveScreenCursorStart,
+ TScreenWinNT::saveScreenCursorEnd;
+unsigned TScreenWinNT::saveScreenCursorX,
+ TScreenWinNT::saveScreenCursorY;
+#else
+ #define SaveScreenReleaseMemory()
+#endif
+DWORD TScreenWinNT::saveScreenConsoleMode;
+unsigned TScreenWinNT::saveScreenWidth,
+ TScreenWinNT::saveScreenHeight;
+
+// Buffer used to arrange the data as needed by Win32 API
+CHAR *TScreenWinNT::outBuf;
+WORD *TScreenWinNT::outBufAttr;
+CHAR_INFO *TScreenWinNT::outBufCI;
+unsigned TScreenWinNT::outBufCapacity;
+
+void TScreenWinNT::ensureOutBufCapacity(unsigned count)
+{
+ count=(count+1) & 0xFFFFFFFE;
+ if (outBufCapacity<count)
+ {
+ free(outBufCI);
+ outBufCI=(CHAR_INFO*)malloc(count*sizeof(CHAR_INFO));
+ outBuf=(CHAR*)outBufCI;
+ outBufAttr=(WORD*)(outBuf+count);
+ outBufCapacity=count;
+ }
+}
+
+int TScreenWinNT::InitOnce()
+{
+ DWORD flags;
+ // Check if we are running in a console
+ if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),&flags))
+ return 0;
+ // SET: On Win32 this value is symbolic, just a number that can't be a
+ // malloced pointer, the screenBuffer isn't used to access the screen.
+ screenBuffer=(ushort *)-1;
+
+ hIn=GetStdHandle(STD_INPUT_HANDLE);
+ #ifdef USE_NEW_BUFFER
+ hStdOut=GetStdHandle(STD_OUTPUT_HANDLE);
+ // Create a new buffer, it have their own content and cursor
+ hOut=CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
+ 0,NULL,CONSOLE_TEXTMODE_BUFFER,NULL);
+ if (hStdOut==INVALID_HANDLE_VALUE || hOut==INVALID_HANDLE_VALUE)
+ return 0; // Something went wrong
+ // Make the new one the active
+ if (!SetConsoleActiveScreenBuffer(hOut))
+ return 0;
+ //hCurrentOut=hOut;
+
+ // If we are here this driver will be used
+ initialized=1;
+ if (dCB) dCB();
+
+ // Console mode
+ GetConsoleMode(hIn,&saveScreenConsoleMode);
+ SetConsoleCtrlHandler(ConsoleEventHandler,TRUE);
+ SetConsoleMode(hIn,TV_CONSOLE_MODE);
+ #else
+ hOut=GetStdHandle(STD_OUTPUT_HANDLE);
+ #endif
+
+ TDisplayWinNT::Init();
+
+ setCharacter=SetCharacter;
+ setCharacters=SetCharacters;
+ getCharacter=GetCharacter;
+ getCharacters=GetCharacters;
+ TScreen::System_p=System;
+ TScreen::Resume=Resume;
+ TScreen::Suspend=Suspend;
+ setCrtModeRes_p=SetCrtModeRes;
+
+ TVWin32Clipboard::Init();
+ TGKeyWinNT::Init();
+ THWMouseWinNT::Init();
+
+ // Cache these values
+ GetCursorShapeLow(curStart,curEnd);
+ GetCursorPosLow(currentCursorX,currentCursorY);
+
+ UINT outCP=GetConsoleOutputCP();
+ UINT inCP=GetConsoleCP();
+ // Look for user settings
+ optSearch("AppCP",forcedAppCP);
+ optSearch("ScrCP",forcedScrCP);
+ optSearch("InpCP",forcedInpCP);
+ // User settings have more priority than detected settings
+ codePage=new TVCodePage(forcedAppCP!=-1 ? forcedAppCP : outCP,
+ forcedScrCP!=-1 ? forcedScrCP : outCP,
+ forcedInpCP!=-1 ? forcedInpCP : inCP);
+ SetDefaultCodePages(outCP,outCP,inCP);
+
+ return 1;
+}
+
+
+TScreenWinNT::TScreenWinNT()
+{
+ if (!InitOnce()) return;
+ flags0=CodePageVar | CursorShapes | CanSetVideoSize;
+ startupMode=getCrtMode();
+ #ifdef USE_NEW_BUFFER
+ // Just remmember the current window size
+ saveScreenWidth =GetCols();
+ saveScreenHeight=GetRows();
+ #else
+ Resume();
+ #endif
+
+ unsigned maxX=saveScreenWidth, maxY=saveScreenHeight;
+ long aux;
+ if (optSearch("ScreenWidth",aux))
+ maxX=aux;
+ if (optSearch("ScreenHeight",aux))
+ maxY=aux;
+ if (maxX!=saveScreenWidth || maxY!=saveScreenHeight)
+ {
+ cursorLines=getCursorType();
+ setCrtModeRes(maxX,maxY);
+ }
+
+ screenMode=getCrtMode();
+ setCrtData();
+
+ suspended=0;
+}
+
+TScreenWinNT::~TScreenWinNT()
+{
+ suspend(); // High level suspend to avoid a double call
+ SaveScreenReleaseMemory();
+ free(outBufCI);
+ outBufCI=0;
+ outBuf=NULL;
+ outBufAttr=0;
+ outBufCapacity=0;
+}
+
+void TScreenWinNT::Resume()
+{
+ #ifdef USE_NEW_BUFFER
+ GetConsoleMode(hIn,&saveScreenConsoleMode);
+ // First switch to our handle
+ SetConsoleActiveScreenBuffer(hOut);
+ // Now we can save the current window size
+ saveScreenWidth =GetCols();
+ saveScreenHeight=GetRows();
+ //hCurrentOut=hOut; Not useful
+ // Restore our window size
+ SetCrtModeRes(screenWidth,screenHeight);
+ #else
+ // Save:
+ // Cursor shape
+ GetCursorShapeLow(saveScreenCursorStart,saveScreenCursorEnd);
+ // Cursor position
+ GetCursorPosLow(saveScreenCursorX,saveScreenCursorY);
+ // Console mode
+ GetConsoleMode(hIn,&saveScreenConsoleMode);
+ // Screen content
+ SaveScreen();
+
+ // Restore:
+ // Window size
+ SetCrtModeRes(screenWidth,screenHeight);
+ // Cursor position
+ SetCursorPosLow(currentCursorX,currentCursorY);
+ // Cursor shape
+ SetCursorShapeLow(curStart,curEnd);
+ #endif
+ // In case the size changed and we failed to restore it
+ setCrtData();
+ // Console mode
+ SetConsoleCtrlHandler(ConsoleEventHandler,TRUE);
+ SetConsoleMode(hIn,TV_CONSOLE_MODE);
+}
+
+void TScreenWinNT::Suspend()
+{
+ #ifdef USE_NEW_BUFFER
+ // Restore window size (using our handle!)
+ SetCrtModeRes(saveScreenWidth,saveScreenHeight);
+ // Switch to the original handle
+ SetConsoleActiveScreenBuffer(hStdOut);
+ #else
+ // Restore:
+ // Window size
+ SetCrtModeRes(saveScreenWidth,saveScreenHeight);
+ // If size changed the content
+ RestoreScreen();
+ // Cursor shape
+ SetCursorShape(saveScreenCursorStart,saveScreenCursorEnd);
+ // Cursor position
+ SetCursorPosLow(saveScreenCursorX,saveScreenCursorY);
+ //FlushConsoleInputBuffer(hIn);
+ #endif
+ // Console mode
+ SetConsoleCtrlHandler(ConsoleEventHandler,FALSE);
+ SetConsoleMode(hIn,saveScreenConsoleMode);
+}
+
+ushort TScreenWinNT::GetCharacter(unsigned offset)
+{
+ COORD coord;
+ coord.Y=(SHORT)((offset)/screenWidth);
+ coord.X=(SHORT)((offset)%screenWidth);
+ DWORD cRead;
+ ushort ch;
+
+ ReadConsoleOutputAttribute(hOut,&ch,1,coord,&cRead);
+ ch<<=8;
+ ReadConsoleOutputCharacter(hOut,(CHAR*)&ch,1,coord,&cRead);
+ return ch;
+}
+
+void TScreenWinNT::GetCharacters(unsigned offset, ushort *buf, unsigned count)
+{
+ ensureOutBufCapacity(count);
+ COORD coord;
+ coord.Y=(SHORT)((offset)/screenWidth);
+ coord.X=(SHORT)((offset)%screenWidth);
+ DWORD cRead;
+
+ ReadConsoleOutputAttribute(hOut,outBufAttr,count,coord,&cRead);
+ ReadConsoleOutputCharacter(hOut,outBuf,count,coord,&cRead);
+ for (count=0; count<cRead; count++, buf++)
+ *buf=(ushort)(outBufAttr[count]<<8) | (ushort)(uchar)outBuf[count];
+}
+
+void TScreenWinNT::SetCharacter(unsigned offset, ushort value)
+{
+ ensureOutBufCapacity(1);
+ COORD coord;
+ coord.Y=(SHORT)((offset)/screenWidth);
+ coord.X=(SHORT)((offset)%screenWidth);
+
+ outBufCI[0].Char.AsciiChar=(CHAR)(value & 0xFF);;
+ outBufCI[0].Attributes=(WORD)(value>>8);
+
+ COORD dwBufferSize={1,1};
+ COORD dwBufferCoord={0,0};
+ SMALL_RECT rcWriteRegion={coord.X, coord.Y, coord.X, coord.Y};
+ WriteConsoleOutput(hOut,outBufCI,dwBufferSize,dwBufferCoord,&rcWriteRegion);
+
+ //FillConsoleOutputCharacter(hOut,(CHAR)(value & 0xFF),1,coord,&cWritten);
+ //FillConsoleOutputAttribute(hOut,(WORD)(value >> 8),1,coord,&cWritten);
+}
+
+void TScreenWinNT::SetCharacters(unsigned offset, ushort *values, unsigned count)
+{
+ ensureOutBufCapacity(count);
+ COORD coord;
+ coord.Y=(SHORT)((offset)/screenWidth);
+ coord.X=(SHORT)((offset)%screenWidth);
+ unsigned i;
+
+ for (i=0; i<count; i++, values++)
+ {
+ outBufCI[i].Char.AsciiChar=(CHAR)(*values & 0xFF);
+ outBufCI[i].Attributes=(WORD)(*values>>8);
+ }
+
+ COORD dwBufferSize={count,1};
+ COORD dwBufferCoord={0, 0};
+ SMALL_RECT rcWriteRegion={coord.X, coord.Y, coord.X+count-1, coord.Y};
+ WriteConsoleOutput(hOut,outBufCI,dwBufferSize,dwBufferCoord,&rcWriteRegion);
+}
+
+#ifndef USE_NEW_BUFFER
+void TScreenWinNT::SaveScreen()
+{
+ unsigned rows=GetRows();
+ unsigned cols=GetCols();
+
+ saveScreenSize=rows*cols;
+ free(saveScreenBuf);
+ saveScreenBuf=(ushort *)malloc(saveScreenSize*sizeof(ushort));
+
+ // Temporarily set these variables to let getCharacter work properly
+ uchar screenWidthSave=screenWidth;
+ uchar screenHeightSave=screenHeight;
+ screenWidth=(uchar)cols;
+ screenHeight=(uchar)rows;
+ unsigned row, ofs;
+ for (row=0, ofs=0; row<rows; row++, ofs+=cols)
+ GetCharacters(ofs,saveScreenBuf+ofs,cols);
+ screenWidth=screenWidthSave;
+ screenHeight=screenHeightSave;
+ saveScreenWidth=(uchar)cols;
+ saveScreenHeight=(uchar)rows;
+
+ /*FILE *f=fopen("screen.dat","wb");
+ fwrite(saveScreenBuf,saveScreenSize,sizeof(ushort),f);
+ fclose(f);*/
+}
+
+void TScreenWinNT::RestoreScreen()
+{
+ // Needed to make SetCharacters work OK
+ unsigned actualW=screenWidth, actualH=screenHeight;
+ screenWidth=saveScreenWidth; screenHeight=saveScreenHeight;
+
+ unsigned rows=saveScreenHeight, row, ofs;
+ unsigned cols=saveScreenWidth;
+
+ for (row=0, ofs=0; row<rows; row++, ofs+=cols)
+ SetCharacters(ofs,saveScreenBuf+ofs,cols);
+
+ screenWidth=actualW; screenHeight=actualH;
+}
+
+void TScreenWinNT::SaveScreenReleaseMemory()
+{
+ free(saveScreenBuf);
+ saveScreenBuf=NULL;
+ saveScreenSize=0;
+}
+#endif
+
+void TScreenWinNT::YieldProcessor(int micros)
+{
+ DWORD msecs=micros<0 ? INFINITE : micros/1000;
+ WaitForSingleObject(TScreenWinNT::hIn,msecs);
+}
+
+// May be I should move all the yield processor routines to TScreen
+extern "C" void __tvWin32Yield(int micros)
+{
+ TScreenWinNT::YieldProcessor(micros);
+}
+
+BOOL WINAPI TScreenWinNT::ConsoleEventHandler(DWORD dwCtrlType)
+{
+ if (dwCtrlType==CTRL_C_EVENT || dwCtrlType==CTRL_BREAK_EVENT)
+ return TRUE;
+ return FALSE;
+}
+
+int TScreenWinNT::System(const char *command, pid_t *pidChild, int in, int out,
+ int err)
+{
+ // If the caller asks for redirection replace the requested handles
+ if (in!=-1)
+ dup2(in,STDIN_FILENO);
+ if (out!=-1)
+ dup2(out,STDOUT_FILENO);
+ if (err!=-1)
+ dup2(err,STDERR_FILENO);
+
+ int rc=system(command);
+ SetConsoleMode(hIn, TV_CONSOLE_MODE);
+ // fork mechanism not implemented, indicate the child finished
+ if (pidChild)
+ *pidChild=0;
+ return rc;
+}
+
+TScreen *TV_WinNTDriverCheck()
+{
+ TScreenWinNT *drv=new TScreenWinNT();
+ if (!TScreen::initialized)
+ {
+ delete drv;
+ return 0;
+ }
+ return drv;
+}
+
+/**[txh]********************************************************************
+
+ Description:
+ Change the window size to the desired value. The font size is ignored
+because it can't be controlled by the application, the Win32 API reference
+I have says: "A screen buffer can be any size, limited only by available
+memory. The dimensions of a screen buffer's window cannot exceed the
+corresponding dimensions of either the screen buffer or the maximum window
+that can fit on the screen based on the current font size (controlled
+exclusively by the user).".@*
+ It only works if we are windowed and this will prevent from going full
+screen unless Windows knows an equivalent text mode.
+
+ Return: 0 no change, 1 full change, 2 approx. change. by SET
+
+***************************************************************************/
+
+int TScreenWinNT::SetCrtModeRes(unsigned w, unsigned h, int fW, int fH)
+{
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ // Find current size
+ if (!GetConsoleScreenBufferInfo(hCurrentOut,&info)) return 0;
+ // Is the same used?
+ if (info.dwSize.X==(int)w && info.dwSize.Y==(int)h)
+ {
+ DBPr3("Already using %d,%d size\n",w,h);
+ return 0;
+ }
+ // Find the max. size, depends on the font and screen size.
+ COORD max=GetLargestConsoleWindowSize(hCurrentOut);
+ COORD newSize={w,h};
+ if (newSize.X>max.X) newSize.X=max.X;
+ if (newSize.Y>max.Y) newSize.Y=max.Y;
+ // The buffer must be large enough to hold both modes (current and new)
+ COORD newBufSize=newSize;
+ if (info.dwMaximumWindowSize.X>newBufSize.X)
+ newBufSize.X=info.dwMaximumWindowSize.X;
+ if (info.dwMaximumWindowSize.Y>newBufSize.Y)
+ newBufSize.Y=info.dwMaximumWindowSize.Y;
+ // Enlarge the buffer size. It fails if not windowed.
+ if (!SetConsoleScreenBufferSize(hCurrentOut,newBufSize)) return 0;
+ // Resize the window.
+ SMALL_RECT r={0,0,newSize.X-1,newSize.Y-1};
+ if (!SetConsoleWindowInfo(hCurrentOut,TRUE,&r))
+ {// Revert buffer size
+ newSize.X=info.dwMaximumWindowSize.X;
+ newSize.Y=info.dwMaximumWindowSize.Y;
+ SetConsoleScreenBufferSize(hCurrentOut,newSize);
+ return 0;
+ }
+ // Now we can shrink the buffer to the needed size
+ SetConsoleScreenBufferSize(hCurrentOut,newSize);
+ // This is something silly TV code spects: after a video mode change the
+ // cursor should go to the "default" state.
+ setCursorType(cursorLines);
+ // Ok! we did it.
+ return fW!=-1 || fH!=-1 || newSize.X!=(int)w || newSize.Y!=(int)h ? 2 : 1;
+}
+
+#else
+
+#include <tv/winnt/screen.h>
+#include <tv/winnt/mouse.h>
+#include <tv/winnt/key.h>
+
+#endif // TVOSf_WIN32
+/*
+Win32 API reference names 45 code pages. Only 20 of them are supported.
+
+Code page identifiers:
+
+*037 EBCDIC
+ 437 MS-DOS United States *
+*500 EBCDIC "500V1"
+*708 Arabic (ASMO 708)
+*709 Arabic (ASMO 449+, BCON V4)
+*710 Arabic (Transparent Arabic)
+*720 Arabic (Transparent ASMO)
+ 737 Greek (formerly 437G) *
+ 775 Baltic *
+ 850 MS-DOS Multilingual (Latin I) *
+ 852 MS-DOS Slavic (Latin II) *
+ 855 IBM Cyrillic (primarily Russian*
+ 857 IBM Turkish *
+ 860 MS-DOS Portuguese
+ 861 MS-DOS Icelandic
+*862 Hebrew
+ 863 MS-DOS Canadian-French
+*864 Arabic
+ 865 MS-DOS Nordic
+ 866 MS-DOS Russian *
+ 869 IBM Modern Greek *
+*874 Thai
+*875 EBCDIC
+*932 Japan
+*936 Chinese (PRC, Singapore)
+*949 Korean
+*950 Chinese (Taiwan, Hong Kong)
+*1026 EBCDIC
+*1200 Unicode (BMP of ISO 10646)
+ 1250 Windows 3.1 Eastern European *
+ 1251 Windows 3.1 Cyrillic *
+ 1252 Windows 3.1 US (ANSI) *
+ 1253 Windows 3.1 Greek *
+ 1254 Windows 3.1 Turkish *
+*1255 Hebrew
+*1256 Arabic
+ 1257 Baltic *
+*1361 Korean (Johab)
+*10000 Macintosh Roman
+*10001 Macintosh Japanese
+*10006 Macintosh Greek I
+ 10007 Macintosh Cyrillic
+*10029 Macintosh Latin 2
+*10079 Macintosh Icelandic
+*10081 Macintosh Turkish
+-----------------
+My registry also says:
+
+20866
+28591
+28592
+28595
+28597
+
+As a Windows 9x is shipped working with only one code page I can't test what
+a hell are these code pages. They seems to be some variant of 866 and some
+kind of ISO cp, just a guess.
+*/
+