我正在尝试制作一个fortan代码,用于在Windows 10系统的命令提示符中显示二维图形的彩色ASCII艺术,就像下面网站上发布的代码一样。
https://sites.google.com/site/akohlmey/random-hacks/text-mode-graphics-for-fortran
我听说Windows 10上的命令提示符部分支持ANSI转义序列,如果我们启用虚拟终端处理选项,它是可用的。
C代码中有一些例子,但我想通过Fortran代码创建一个函数或子例程来启用ANSI转义序列。例如,用Fortran函数(或子程序(重写下面的简单C函数有困难吗?
bool enable_virtual_terminal_processing(FILE *stream) {
HANDLE handle = (HANDLE)_get_osfhandle(_fileno(stream));
DWORD mode = 0;
if (!GetConsoleMode(handle, &mode)) {
return false;
}
if (!SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
return false;
}
return true;
}
以下Fortran源代码调用必要的Windows API以将控制台设置为正确模式的示例。调用Windows API使该源本质上特定于平台-还需要一些编译器特定指令(提供ifort和gfortran变体(来完全定义API接口。
Win32内核API的库也需要是可访问的,但这通常是作为在Windows上安装编译器的一部分安装的(或必需的(。
有些编译器为Windows API提供了带有相关接口的模块,从而省去了您自己编写这些模块的麻烦。
PROGRAM WriteAnsi
IMPLICIT NONE
CALL set_ansi
PRINT "(A)", &
ACHAR(27) // '[31m' // 'H' // &
ACHAR(27) // '[32m' // 'e' // &
ACHAR(27) // '[33m' // 'l' // &
ACHAR(27) // '[34m' // 'l' // &
ACHAR(27) // '[35m' // 'o' // &
ACHAR(27) // '[0m'
CONTAINS
SUBROUTINE set_ansi
USE, INTRINSIC :: ISO_C_BINDING, ONLY: &
DWORD => C_LONG, & ! C_INT32_T really, but this is per the docs
HANDLE => C_INTPTR_T, &
BOOL => C_INT
INTEGER(HANDLE), PARAMETER :: INVALID_HANDLE_VALUE = -1_HANDLE
INTERFACE
FUNCTION GetStdHandle(nStdHandle) BIND(C, NAME='GetStdHandle')
IMPORT :: DWORD
IMPORT :: HANDLE
IMPLICIT NONE
INTEGER(DWORD), INTENT(IN), VALUE :: nStdHandle
INTEGER(HANDLE) :: GetStdHandle
!DEC$ ATTRIBUTES STDCALL :: GetStdHandle
!GCC$ ATTRIBUTES STDCALL :: GetStdHandle
END FUNCTION GetStdHandle
END INTERFACE
INTEGER(DWORD), PARAMETER :: STD_INPUT_HANDLE = -10_DWORD
INTEGER(DWORD), PARAMETER :: STD_OUTPUT_HANDLE = -11_DWORD
INTEGER(DWORD), PARAMETER :: STD_ERROR_HANDLE = -12_DWORD
INTERFACE
FUNCTION GetConsoleMode(hConsoleHandle, lpMode) BIND(C, NAME='GetConsoleMode')
IMPORT :: HANDLE
IMPORT :: DWORD
IMPORT :: BOOL
IMPLICIT NONE
INTEGER(HANDLE), INTENT(IN), VALUE :: hConsoleHandle
INTEGER(DWORD), INTENT(OUT) :: lpMode
!DEC$ ATTRIBUTES REFERENCE :: lpMode
INTEGER(BOOL) :: GetConsoleMode
!DEC$ ATTRIBUTES STDCALL :: GetConsoleMode
!GCC$ ATTRIBUTES STDCALL :: GetConsoleMode
END FUNCTION GetConsoleMode
END INTERFACE
INTEGER(DWORD), PARAMETER :: ENABLE_ECHO_INPUT = INT(Z'0004', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_INSERT_MODE = INT(Z'0020', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_LINE_INPUT = INT(Z'0002', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_MOUSE_INPUT = INT(Z'0010', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_PROCESSED_INPUT = INT(Z'0001', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_QUICK_EDIT_MODE = INT(Z'0040', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_WINDOW_INPUT = INT(Z'0008', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_VIRTUAL_TERMINAL_INPUT = INT(Z'0200', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_PROCESSED_OUTPUT = INT(Z'0001', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_WRAP_AT_EOL_OUTPUT = INT(Z'0002', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_VIRTUAL_TERMINAL_PROCESSING = INT(Z'0004', DWORD)
INTEGER(DWORD), PARAMETER :: DISABLE_NEWLINE_AUTO_RETURN = INT(Z'00008', DWORD)
INTEGER(DWORD), PARAMETER :: ENABLE_LVB_GRID_WORLDWIDE = INT(Z'0010', DWORD)
INTERFACE
FUNCTION SetConsoleMode(hConsoleHandle, dwMode) BIND(C, NAME='SetConsoleMode')
IMPORT :: HANDLE
IMPORT :: DWORD
IMPORT :: BOOL
IMPLICIT NONE
INTEGER(HANDLE), INTENT(IN), VALUE :: hConsoleHandle
INTEGER(DWORD), INTENT(IN), VALUE :: dwMode
INTEGER(BOOL) :: SetConsoleMode
!DEC$ ATTRIBUTES STDCALL :: SetConsoleMode
!GCC$ ATTRIBUTES STDCALL :: SetConsoleMode
END FUNCTION SetConsoleMode
END INTERFACE
INTEGER(DWORD), PARAMETER :: ENABLE_EXTENDED_FLAGS = INT(Z'0080', DWORD)
INTEGER(HANDLE) :: output_handle
INTEGER(BOOL) :: api_result
INTEGER(DWORD) :: mode
output_handle = GetStdHandle(STD_OUTPUT_HANDLE)
IF (output_handle == INVALID_HANDLE_VALUE) THEN
ERROR STOP 'GetStdHandle failed'
END IF
api_result = GetConsoleMode(output_handle, mode)
IF (api_result == 0_BOOL) THEN
ERROR STOP 'GetConsoleMode failed'
END IF
api_result = SetConsoleMode( &
output_handle, &
IOR(mode, ENABLE_VIRTUAL_TERMINAL_PROCESSING) )
IF (api_result == 0_BOOL) THEN
ERROR STOP 'SetConsoleMode failed'
END IF
END SUBROUTINE set_ansi
END PROGRAM WriteAnsi
从Fortran调用C函数很容易(这里有数百个问题和答案(。
在Fortran中几乎不可能写出上面的内容,这是系统编程,你需要合适的库。也许有些编译器提供了它们,但这不是标准的。
只需从Fortran中调用C函数。FILE *
参数不能直接在Fortran中使用,您还需要在C中获取指针。
启用后,您可以非常容易地编写序列如何在fortran 中的fortran clear屏幕中的相同位置打印