Files
harbour-core/harbour/source/rtl/gt/gtwin.c

690 lines
20 KiB
C

/*
* $Id$
*/
/*
* Harbour Project source code:
* Video subsystem for Win32 compilers
*
* Copyright 1999 Paul Tucker <ptucker@sympatico.ca> (functions marked ptucker)
*
* www - http://www.harbour-project.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version, with one exception:
*
* The exception is that if you link the Harbour Runtime Library (HRL)
* and/or the Harbour Virtual Machine (HVM) with other files to produce
* an executable, this does not by itself cause the resulting executable
* to be covered by the GNU General Public License. Your use of that
* executable is in no way restricted on account of linking the HRL
* and/or HVM code into it.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit
* their web site at http://www.gnu.org/).
*
*/
/*
* The following parts are Copyright of the individual authors.
* www - http://www.harbour-project.org
*
* Copyright 1999 Victor Szel <info@szelvesz.hu>
* hb_gt_CtrlHandler()
*
* See doc/license.txt for licensing terms.
*
*/
/*
* Portions of this module are based (somewhat) on VIDMGR by
* Andrew Clarke and modified for the Harbour project
*/
/* NOTE: User programs should never call this layer directly! */
#include <stdlib.h>
#include <string.h>
#define WIN32_LEAN_AND_MEAN
#if defined(__GNUC__)
#define HB_DONT_DEFINE_BASIC_TYPES
#endif /* __GNUC__ */
#include <windows.h>
#include "gtapi.h"
#include "set.h" /* For Ctrl+Break handling */
#include "ctoharb.h" /* For Ctrl+Break handling */
#if defined(__IBMCPP__)
#undef WORD /* 2 bytes unsigned */
typedef unsigned short int WORD;
#else
#if ! defined(HB_DONT_DEFINE_BASIC_TYPES)
#undef WORD /* 2 bytes unsigned */
typedef unsigned short int WORD;
#undef DWORD /* 4 bytes unsigned */
typedef unsigned long DWORD;
#endif
#endif
#if ! defined(__GNUC__)
#ifdef __CYGWIN__
typedef WORD far * LPWORD;
#endif
#endif /* __GNUC__ */
static BOOL hb_gt_SetScreenBuffer( HANDLE HNew, HANDLE HOld );
static HANDLE HOsave;
/* static HANDLE HSsave; */
static HANDLE HDOutput = INVALID_HANDLE_VALUE;
/* static HANDLE HDStealth = INVALID_HANDLE_VALUE; */
HANDLE hb_gtHInput = INVALID_HANDLE_VALUE; /* hb_inkeyPoll() needs this */
BOOL hb_gtBreak = FALSE; /* Used to signal Ctrl+Break to hb_inkeyPoll() */
static HANDLE HOutput = INVALID_HANDLE_VALUE;
static HANDLE HStealth = INVALID_HANDLE_VALUE; /* DispBegin buffer */
static HANDLE HOriginal; /* used to restore before quit */
static HANDLE HCursor; /* When DispBegin is in effect, all cursor related
functions must refer to the active handle!
Otherwise turds are left on the screen when
running in a window. This handle will always
refer to the currently _active_ buffer which could
be different than the one being written to.
*/
static BOOL WINAPI hb_gt_CtrlHandler( DWORD dwCtrlType )
{
BOOL bHandled;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_CtrlHandler(%lu)", (unsigned long) dwCtrlType));
switch( dwCtrlType )
{
case CTRL_C_EVENT:
bHandled = FALSE;
break;
case CTRL_BREAK_EVENT:
hb_gtBreak = TRUE;
bHandled = TRUE;
break;
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
default:
bHandled = FALSE;
}
return bHandled;
}
void hb_gt_Init( void )
{
HB_TRACE(HB_TR_DEBUG, ("hb_gt_Init()"));
if( ( hb_gtHInput = GetStdHandle( STD_INPUT_HANDLE ) ) == INVALID_HANDLE_VALUE )
{
if( hb_dynsymFindName( "__DBGENTRY" ) ) /* the debugger is linked */
{
AllocConsole(); /* It is a Windows app without a console, so we create one */
hb_gtHInput = GetStdHandle( STD_INPUT_HANDLE );
}
}
SetConsoleMode( hb_gtHInput, 0 );
/* ptucker */
HOriginal = HOutput = HCursor = CreateFile( "CONOUT$", /* filename */
GENERIC_READ | GENERIC_WRITE, /* Access flag */
FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
NULL, /* security attributes */
OPEN_EXISTING, /* create mode */
0, 0 );
/* Add Ctrl+Break handler [vszel] */
SetConsoleCtrlHandler( hb_gt_CtrlHandler, TRUE );
}
void hb_gt_Done( void )
{
HB_TRACE(HB_TR_DEBUG, ("hb_gt_Done()"));
if( HOutput != HOriginal )
{
/* ptucker */
/* because the current screen may not be the one that was active
when the app started, we need to restore that screen and update
it with the current image before quitting.
*/
/* easy fix ;-) */
hb_gtDispBegin(); /* must use these versions ! */
hb_gtDispEnd();
}
/* NOTE: There's no need to close these explicitly, moreover if we close them
functions using stdout will not show anything.
CloseHandle( hb_gtHInput );
hb_gtHInput = INVALID_HANDLE_VALUE;
CloseHandle( HOutput );
HOutput = INVALID_HANDLE_VALUE;
*/
if( HStealth != INVALID_HANDLE_VALUE )
{
CloseHandle( HStealth );
HStealth = INVALID_HANDLE_VALUE;
}
/* Remove Ctrl+Break handler [vszel] */
SetConsoleCtrlHandler( hb_gt_CtrlHandler, FALSE );
}
BOOL hb_gt_IsColor( void )
{
HB_TRACE(HB_TR_DEBUG, ("hb_gt_IsColor()"));
/* TODO: need to call something to do this instead of returning TRUE */
return TRUE;
}
USHORT hb_gt_GetScreenWidth( void )
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_GetScreenWidth()"));
GetConsoleScreenBufferInfo( HOutput, &csbi );
/* return csbi.dwMaximumWindowSize.X; */
/* return max( csbi.srWindow.Right - csbi.srWindow.Left + 1, 40 ); */
return max( csbi.dwSize.X, 40 );
}
USHORT hb_gt_GetScreenHeight( void )
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_GetScreenHeight()"));
GetConsoleScreenBufferInfo( HOutput, &csbi );
/* return csbi.dwMaximumWindowSize.Y; */
/* return max( csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 25 ); */
return max( csbi.dwSize.Y, 25 );
}
void hb_gt_SetPos( USHORT uiRow, USHORT uiCol )
{
COORD dwCursorPosition;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_SetPos(%hu, %hu)", uiRow, uiCol));
dwCursorPosition.X = ( SHORT ) uiCol;
dwCursorPosition.Y = ( SHORT ) uiRow;
SetConsoleCursorPosition( HCursor, dwCursorPosition );
}
USHORT hb_gt_GetCursorStyle( void )
{
CONSOLE_CURSOR_INFO cci;
USHORT uiCursorShape;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_GetCursorStyle()"));
GetConsoleCursorInfo( HCursor, &cci );
if( ! cci.bVisible )
{
uiCursorShape = SC_NONE;
}
else
{
switch( cci.dwSize )
{
case 50:
uiCursorShape = SC_INSERT; /* half block in clipper */
break;
case 99:
uiCursorShape = SC_SPECIAL1; /* full block in clipper */
break;
case 66:
uiCursorShape = SC_SPECIAL2; /* upper half block in clipper */
break;
/* TODO: cannot tell if the block is upper or lower for cursor */
/* Answer: Supposed to be upper third, but ms don't support it. */
default:
uiCursorShape = SC_NORMAL; /* anything else, we'll call it normal */
break;
}
}
return uiCursorShape;
}
void hb_gt_SetCursorStyle( USHORT style )
{
CONSOLE_CURSOR_INFO cci;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_SetCursorStyle(%hu)", style));
GetConsoleCursorInfo( HCursor, &cci );
switch( style )
{
case SC_NONE:
cci.bVisible = FALSE;
break;
case SC_INSERT:
cci.bVisible = TRUE;
cci.dwSize = 50;
break;
case SC_SPECIAL1:
cci.bVisible = TRUE;
cci.dwSize = 99;
break;
case SC_SPECIAL2:
cci.bVisible = TRUE;
cci.dwSize = 66;
/* In their infinite wisdom, MS doesn't support cursors that
don't start at the bottom of the cell */
break;
case SC_NORMAL:
default: /* traps for invalid values */
cci.bVisible = TRUE;
cci.dwSize = 25; /* this was 12, but when used in full screen dos window
cursor state is erratic - doesn't turn off, etc. */
break;
}
SetConsoleCursorInfo( HCursor, &cci );
}
void hb_gt_Puts( USHORT uiRow, USHORT uiCol, BYTE attr, BYTE * str, ULONG len )
{
DWORD dwWritten;
COORD coord;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_Puts(%hu, %hu, %d, %p, %lu)", uiRow, uiCol, (int) attr, str, len));
coord.X = ( DWORD ) uiCol;
coord.Y = ( DWORD ) uiRow;
FillConsoleOutputAttribute( HOutput, ( WORD )( attr & 0xFF ), ( DWORD ) len, coord, &dwWritten );
WriteConsoleOutputCharacterA( HOutput, ( char * ) str, ( DWORD ) len, coord, &dwWritten );
}
void hb_gt_GetText( USHORT uiTop, USHORT uiLeft, USHORT uiBottom, USHORT uiRight, BYTE * dest )
{
LPWORD pwattr;
BYTE * pstr;
USHORT width;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_GetText(%hu, %hu, %hu, %hu, %p)", uiTop, uiLeft, uiBottom, uiRight, dest));
width = ( uiRight - uiLeft + 1 );
pwattr = ( LPWORD ) hb_xgrab( width * sizeof( *pwattr ) );
pstr = ( BYTE * ) hb_xgrab( width );
for( ; uiTop <= uiBottom; uiTop++ )
{
COORD coord;
USHORT i;
DWORD dwWritten;
coord.X = ( DWORD ) uiLeft;
coord.Y = ( DWORD ) uiTop;
ReadConsoleOutputCharacterA( HOutput, ( char * ) pstr, width, coord, &dwWritten );
ReadConsoleOutputAttribute( HOutput, pwattr, width, coord, &dwWritten );
for( i = 0; i < width; i++ )
{
*dest = *( pstr + i );
dest++;
*dest = ( BYTE ) *( pwattr + i ) & 0xFF;
dest++;
}
}
hb_xfree( pstr );
hb_xfree( pwattr );
}
void hb_gt_PutText( USHORT uiTop, USHORT uiLeft, USHORT uiBottom, USHORT uiRight, BYTE * srce )
{
LPWORD pwattr;
BYTE * pstr;
USHORT width;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_PutText(%hu, %hu, %hu, %hu, %p)", uiTop, uiLeft, uiBottom, uiRight, srce));
width = ( uiRight - uiLeft + 1 );
pwattr = ( LPWORD ) hb_xgrab( width * sizeof( *pwattr ) );
pstr = ( BYTE * ) hb_xgrab( width );
for( ; uiTop <= uiBottom; uiTop++ )
{
COORD coord;
USHORT i;
DWORD dwWritten;
for( i = 0; i < width; i++ )
{
*( pstr + i ) = *srce;
srce++;
*( pwattr + i ) = ( ( WORD )( ( BYTE ) *srce ) & 0xFF );
srce++;
}
coord.X = ( DWORD ) uiLeft;
coord.Y = ( DWORD ) uiTop;
WriteConsoleOutputAttribute( HOutput, pwattr, width, coord, &dwWritten );
WriteConsoleOutputCharacterA( HOutput, ( char * ) pstr, width, coord, &dwWritten );
}
hb_xfree( pstr );
hb_xfree( pwattr );
}
void hb_gt_SetAttribute( USHORT uiTop, USHORT uiLeft, USHORT uiBottom, USHORT uiRight, BYTE attr )
{
/* ptucker */
COORD coord;
USHORT width;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_SetAttribute(%hu, %hu, %hu, %hu, %d)", uiTop, uiLeft, uiBottom, uiRight, (int) attr));
width = uiRight - uiLeft + 1;
coord.X = ( DWORD ) uiLeft;
for( ; uiTop <= uiBottom; uiTop++ )
{
DWORD dwWritten;
coord.Y = uiTop;
FillConsoleOutputAttribute( HOutput, ( WORD )( attr & 0xFF ), width, coord, &dwWritten );
}
}
USHORT hb_gt_Col( void )
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_Col()"));
GetConsoleScreenBufferInfo( HCursor, &csbi );
return csbi.dwCursorPosition.X;
}
USHORT hb_gt_Row( void )
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_Row()"));
GetConsoleScreenBufferInfo( HCursor, &csbi );
return csbi.dwCursorPosition.Y;
}
void hb_gt_Scroll( USHORT uiTop, USHORT uiLeft, USHORT uiBottom, USHORT uiRight, BYTE attr, SHORT iVert, SHORT iHoriz )
{
/* ptucker */
HB_TRACE(HB_TR_DEBUG, ("hb_gt_Scroll(%hu, %hu, %hu, %hu, %d, %hd, %hd)", uiTop, uiLeft, uiBottom, uiRight, (int) attr, iVert, iHoriz));
if( ( iHoriz | iVert ) == 0 ) /* both zero? */
{
COORD coord;
USHORT width = uiRight - uiLeft + 1;
coord.X = ( DWORD ) uiLeft;
for( ; uiTop <= uiBottom; uiTop++ )
{
DWORD dwWritten;
coord.Y = uiTop;
FillConsoleOutputAttribute( HOutput, ( WORD )( attr & 0xFF ), width, coord, &dwWritten );
FillConsoleOutputCharacter( HOutput, ' ', width, coord, &dwWritten );
}
}
else
{
SMALL_RECT Source, Clip;
COORD Target;
CHAR_INFO FillChar;
Source.Top = uiTop;
Source.Left = uiLeft;
Source.Bottom = uiBottom;
Source.Right = uiRight;
memcpy( &Clip, &Source, sizeof( SMALL_RECT ) );
Target.Y = uiTop - iVert;
Target.X = uiLeft - iHoriz;
FillChar.Char.AsciiChar = ' ';
FillChar.Attributes = ( WORD )( attr & 0xFF );
ScrollConsoleScreenBuffer( HOutput, &Source, &Clip, Target, &FillChar );
}
}
void hb_gt_DispBegin( void )
{
HB_TRACE(HB_TR_DEBUG, ("hb_gt_DispBegin()"));
/* ptucker */
if( hb_gtDispCount() == 1 )
{
COORD coDest = { 0, 0 };
COORD coBuf; /* the size of the buffer to read into */
CHAR_INFO * pCharInfo; /* buffer to store info from ReadConsoleOutput */
SMALL_RECT srWin; /* source rectangle to read from */
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo( HCursor, &csbi );
srWin.Top = srWin.Left = 0;
srWin.Bottom = ( coBuf.Y = csbi.dwSize.Y ) - 1;
srWin.Right = ( coBuf.X = csbi.dwSize.X ) - 1;
/* allocate a buffer for the screen rectangle */
pCharInfo = ( CHAR_INFO * ) hb_xgrab( coBuf.Y * coBuf.X * sizeof( CHAR_INFO ) );
/* read the screen rectangle into the buffer */
ReadConsoleOutput( HOutput, /* current screen handle */
pCharInfo, /* transfer area */
coBuf, /* size of destination buffer */
coDest, /* upper-left cell to write data to */
&srWin ); /* screen buffer rectangle to read from */
if( HStealth == INVALID_HANDLE_VALUE )
{
HStealth = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE, /* Access flag */
FILE_SHARE_READ | FILE_SHARE_WRITE, /* Buffer share mode */
NULL, /* Security attribute */
CONSOLE_TEXTMODE_BUFFER, /* Type of buffer */
NULL ); /* reserved */
}
hb_gt_SetScreenBuffer( HStealth, HOutput );
HOutput = HStealth;
WriteConsoleOutput( HOutput, /* output handle */
pCharInfo, /* data to write */
coBuf, /* col/row size of source buffer */
coDest, /* upper-left cell to write data from in src */
&srWin ); /* screen buffer rect to write data to */
hb_xfree( pCharInfo );
}
}
void hb_gt_DispEnd( void )
{
HB_TRACE(HB_TR_DEBUG, ("hb_gt_DispEnd()"));
/* ptucker */
if( hb_gtDispCount() == 1 )
{
HANDLE htmp = HStealth;
HStealth = HCursor;
hb_gt_DispBegin();
HStealth = htmp;
}
}
static BOOL hb_gt_SetScreenBuffer( HANDLE HNew, HANDLE HOld )
{
/* ptucker */
/* set a new buffer to have the same characteristics as an existing buffer */
CONSOLE_SCREEN_BUFFER_INFO csbi;
SMALL_RECT srWin;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_SetScreenBuffer(%p, %p)", HNew, HOld));
GetConsoleScreenBufferInfo( HOld, &csbi );
/* new console window size and scroll position */
srWin.Top = srWin.Left = 0;
srWin.Bottom = csbi.dwSize.Y - 1;
srWin.Right = csbi.dwSize.X - 1;
SetConsoleScreenBufferSize( HNew, csbi.dwSize );
SetConsoleWindowInfo( HNew, TRUE, &csbi.srWindow );
SetConsoleWindowInfo( HNew, FALSE, &srWin );
return TRUE;
}
BOOL hb_gt_SetMode( USHORT uiRows, USHORT uiCols )
{
/* ptucker */
BOOL bRetVal = TRUE;
CONSOLE_SCREEN_BUFFER_INFO csbi;
SMALL_RECT srWin;
COORD coBuf;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_SetMode(%hu, %hu)", uiRows, uiCols));
GetConsoleScreenBufferInfo( HOutput, &csbi );
coBuf = GetLargestConsoleWindowSize( HOutput );
/* new console window size and scroll position */
srWin.Top = srWin.Left = 0;
srWin.Bottom = ( SHORT ) ( min( uiRows, coBuf.Y ) - 1 );
srWin.Right = ( SHORT ) ( min( uiCols, coBuf.X ) - 1 );
/* new console buffer size */
coBuf.Y = uiRows;
coBuf.X = uiCols;
/* if the current buffer is larger than what we want, resize the */
/* console window first, then the buffer */
if( ( DWORD ) csbi.dwSize.X * csbi.dwSize.Y > ( DWORD ) uiCols * uiRows )
{
if( !SetConsoleWindowInfo( HOutput, TRUE, &srWin ) ||
!SetConsoleScreenBufferSize( HOutput, coBuf ) )
bRetVal = FALSE;
}
else if( ( DWORD ) csbi.dwSize.X * csbi.dwSize.Y < ( DWORD ) uiCols * uiRows )
{
if( !SetConsoleScreenBufferSize( HOutput, coBuf ) ||
!SetConsoleWindowInfo( HOutput, TRUE, &srWin ) )
bRetVal = FALSE;
}
return bRetVal;
}
void hb_gt_Replicate( BYTE c, ULONG ulLength )
{
/* ptucker */
COORD coBuf = { 0, 0 };
DWORD dwWritten;
HB_TRACE(HB_TR_DEBUG, ("hb_gt_Replicate(%d, %lu)", (int) c, ulLength));
/* TODO: This is not used and may be eliminated after further review */
FillConsoleOutputCharacter(
HOutput, /* handle to screen buffer */
c, /* character to write */
( DWORD ) ulLength, /* number of cells to write */
coBuf, /* coordinates of first cell */
&dwWritten /* receives actual number written */
);
}
BOOL hb_gt_GetBlink()
{
HB_TRACE(HB_TR_DEBUG, ("hb_gt_GetBlink()"));
/* TODO */
return FALSE;
}
void hb_gt_SetBlink( BOOL bBlink )
{
HB_TRACE(HB_TR_DEBUG, ("hb_gt_SetBlink(%d)", (int) bBlink));
/* TODO: set the bit if it's supported */
HB_SYMBOL_UNUSED( bBlink );
}
void hb_gt_DebugScreen( BOOL bActivate )
{
HB_TRACE(HB_TR_DEBUG, ("hb_gt_DebugScreen(%d)", (int) bActivate));
/* ptucker */
/* TODO: This is not used and is still a work in progress */
if( bActivate )
{
if( HDOutput == INVALID_HANDLE_VALUE )
{
HDOutput = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE, /* Access flag */
FILE_SHARE_READ | FILE_SHARE_WRITE, /* Buffer share mode */
NULL, /* Security attribute */
CONSOLE_TEXTMODE_BUFFER, /* Type of buffer */
NULL ); /* reserved */
hb_gt_SetScreenBuffer( HDOutput, HOutput );
}
HOsave = HOutput;
HOutput = HCursor = HDOutput;
hb_gtDispBegin();
hb_gtDispEnd();
}
else
{
HOutput = HOsave;
HCursor = HOriginal;
}
SetConsoleActiveScreenBuffer( HOutput );
}