【问题标题】:How to enable ANSI escape sequences in command prompt on Windows 10 by Fortran code?如何通过 Fortran 代码在 Windows 10 的命令提示符下启用 ANSI 转义序列?
【发布时间】:2019-11-19 03:35:06
【问题描述】:

我正在尝试制作一个 fortan 代码,用于在 Windows 10 系统的命令提示符下显示 2D 图形的彩色 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;
}

【问题讨论】:

标签: windows fortran


【解决方案1】:

下面的 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

【讨论】:

  • @lanH 很抱歉我的回复延迟,我非常感谢您的“完美”Fortran 代码示例。我已经编译并测试了它。首先,“SetConsoleMode”函数调用失败,因为我的命令提示符环境设置为旧控制台模式。但是,在禁用旧控制台模式后,我可以获得正确的结果,其中彩色字符显示在命令提示符窗口上。非常感谢您再次提供漂亮的代码!
【解决方案2】:

从 Fortran 调用 C 函数很容易(这里有数百个问题和答案)。

用 Fortran 编写上面的代码几乎是不可能的,它是系统编程,你需要正确的库也许有些编译器提供它们,但这不是标准的。

只需从 Fortran 调用您的 C 函数。 FILE * 参数不会直接在 Fortran 中可用,您还需要在 C 中获取指针。

启用后,你可以很容易地编写序列How to print in the same position in fortranclear screen in Fortran

【讨论】:

  • 非常感谢您的及时建议。根据您的 cmets,我觉得从 Fortran 调用外部 C 函数是一种更好的方法。但是,虽然它可能不是标准和兼容的方式,但如果编译器提供了一个好的库,我想使用它。你知道英特尔 Fortran 编译器是否提供了这样的库吗? (我使用英特尔编译器 v18。)
  • 我非常怀疑是否有专门用于此的库。还有其他用于 Windows 特定任务的库(一些简单的窗口),但您必须查看您的手册。
  • 非常感谢您的及时回复。我想查看编译器的手册,同时尝试前一种想法(即从 Fortran 调用 C 函数)。
  • @kachigusa 您可以使用Windows' console API。英特尔 Fortran 与 Windows API 有许多绑定。例如,要设置文本颜色,可以使用 SetConsoleTextAttribute function 函数。另见SetConsoleCursorPosition。还有这个:software.intel.com/en-us/…
猜你喜欢
  • 1970-01-01
  • 2021-04-19
  • 2015-08-14
  • 2020-06-13
  • 1970-01-01
  • 2016-12-11
  • 2020-10-11
  • 2022-11-20
  • 1970-01-01
相关资源
最近更新 更多