| /* PDCurses */ |
| |
| #include "pdcwin.h" |
| |
| #include <stdlib.h> |
| |
| /* Color component table */ |
| |
| PDCCOLOR pdc_color[PDC_MAXCOL]; |
| |
| HANDLE std_con_out = INVALID_HANDLE_VALUE; |
| HANDLE pdc_con_out = INVALID_HANDLE_VALUE; |
| HANDLE pdc_con_in = INVALID_HANDLE_VALUE; |
| |
| DWORD pdc_quick_edit; |
| |
| static short realtocurs[16] = |
| { |
| COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, COLOR_RED, |
| COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE, COLOR_BLACK + 8, |
| COLOR_BLUE + 8, COLOR_GREEN + 8, COLOR_CYAN + 8, COLOR_RED + 8, |
| COLOR_MAGENTA + 8, COLOR_YELLOW + 8, COLOR_WHITE + 8 |
| }; |
| |
| static short ansitocurs[16] = |
| { |
| COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, |
| COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE, COLOR_BLACK + 8, |
| COLOR_RED + 8, COLOR_GREEN + 8, COLOR_YELLOW + 8, COLOR_BLUE + 8, |
| COLOR_MAGENTA + 8, COLOR_CYAN + 8, COLOR_WHITE + 8 |
| }; |
| |
| short pdc_curstoreal[16], pdc_curstoansi[16]; |
| short pdc_oldf, pdc_oldb, pdc_oldu; |
| bool pdc_conemu, pdc_wt, pdc_ansi; |
| |
| enum { PDC_RESTORE_NONE, PDC_RESTORE_BUFFER }; |
| |
| /* Struct for storing console registry keys, and for use with the |
| undocumented WM_SETCONSOLEINFO message. Originally by James Brown, |
| www.catch22.net. */ |
| |
| static struct |
| { |
| ULONG Length; |
| COORD ScreenBufferSize; |
| COORD WindowSize; |
| ULONG WindowPosX; |
| ULONG WindowPosY; |
| |
| COORD FontSize; |
| ULONG FontFamily; |
| ULONG FontWeight; |
| WCHAR FaceName[32]; |
| |
| ULONG CursorSize; |
| ULONG FullScreen; |
| ULONG QuickEdit; |
| ULONG AutoPosition; |
| ULONG InsertMode; |
| |
| USHORT ScreenColors; |
| USHORT PopupColors; |
| ULONG HistoryNoDup; |
| ULONG HistoryBufferSize; |
| ULONG NumberOfHistoryBuffers; |
| |
| COLORREF ColorTable[16]; |
| |
| ULONG CodePage; |
| HWND Hwnd; |
| |
| WCHAR ConsoleTitle[0x100]; |
| } console_info; |
| |
| #ifdef HAVE_NO_INFOEX |
| /* Console screen buffer information (extended version) */ |
| typedef struct _CONSOLE_SCREEN_BUFFER_INFOEX { |
| ULONG cbSize; |
| COORD dwSize; |
| COORD dwCursorPosition; |
| WORD wAttributes; |
| SMALL_RECT srWindow; |
| COORD dwMaximumWindowSize; |
| WORD wPopupAttributes; |
| BOOL bFullscreenSupported; |
| COLORREF ColorTable[16]; |
| } CONSOLE_SCREEN_BUFFER_INFOEX; |
| typedef CONSOLE_SCREEN_BUFFER_INFOEX *PCONSOLE_SCREEN_BUFFER_INFOEX; |
| #endif |
| |
| typedef BOOL (WINAPI *SetConsoleScreenBufferInfoExFn)(HANDLE hConsoleOutput, |
| PCONSOLE_SCREEN_BUFFER_INFOEX lpConsoleScreenBufferInfoEx); |
| typedef BOOL (WINAPI *GetConsoleScreenBufferInfoExFn)(HANDLE hConsoleOutput, |
| PCONSOLE_SCREEN_BUFFER_INFOEX lpConsoleScreenBufferInfoEx); |
| |
| static SetConsoleScreenBufferInfoExFn pSetConsoleScreenBufferInfoEx = NULL; |
| static GetConsoleScreenBufferInfoExFn pGetConsoleScreenBufferInfoEx = NULL; |
| |
| static CONSOLE_SCREEN_BUFFER_INFO orig_scr; |
| static CONSOLE_SCREEN_BUFFER_INFOEX console_infoex; |
| |
| static LPTOP_LEVEL_EXCEPTION_FILTER xcpt_filter; |
| |
| static DWORD old_console_mode = 0; |
| |
| static bool is_nt; |
| |
| static void _reset_old_colors(void) |
| { |
| pdc_oldf = -1; |
| pdc_oldb = -1; |
| pdc_oldu = 0; |
| } |
| |
| static HWND _find_console_handle(void) |
| { |
| TCHAR orgtitle[1024], temptitle[1024]; |
| HWND wnd; |
| |
| GetConsoleTitle(orgtitle, 1024); |
| |
| wsprintf(temptitle, TEXT("%d/%d"), GetTickCount(), GetCurrentProcessId()); |
| SetConsoleTitle(temptitle); |
| |
| Sleep(40); |
| |
| wnd = FindWindow(NULL, temptitle); |
| |
| SetConsoleTitle(orgtitle); |
| |
| return wnd; |
| } |
| |
| /* Undocumented console message */ |
| |
| #define WM_SETCONSOLEINFO (WM_USER + 201) |
| |
| /* Wrapper around WM_SETCONSOLEINFO. We need to create the necessary |
| section (file-mapping) object in the context of the process which |
| owns the console, before posting the message. Originally by JB. */ |
| |
| static void _set_console_info(void) |
| { |
| CONSOLE_SCREEN_BUFFER_INFO csbi; |
| CONSOLE_CURSOR_INFO cci; |
| DWORD dwConsoleOwnerPid; |
| HANDLE hProcess; |
| HANDLE hSection, hDupSection; |
| PVOID ptrView; |
| |
| /* Each-time initialization for console_info */ |
| |
| GetConsoleCursorInfo(pdc_con_out, &cci); |
| console_info.CursorSize = cci.dwSize; |
| |
| GetConsoleScreenBufferInfo(pdc_con_out, &csbi); |
| console_info.ScreenBufferSize = csbi.dwSize; |
| |
| console_info.WindowSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1; |
| console_info.WindowSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; |
| |
| console_info.WindowPosX = csbi.srWindow.Left; |
| console_info.WindowPosY = csbi.srWindow.Top; |
| |
| /* Open the process which "owns" the console */ |
| |
| GetWindowThreadProcessId(console_info.Hwnd, &dwConsoleOwnerPid); |
| |
| hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwConsoleOwnerPid); |
| |
| /* Create a SECTION object backed by page-file, then map a view of |
| this section into the owner process so we can write the contents |
| of the CONSOLE_INFO buffer into it */ |
| |
| hSection = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, |
| 0, sizeof(console_info), 0); |
| |
| /* Copy our console structure into the section-object */ |
| |
| ptrView = MapViewOfFile(hSection, FILE_MAP_WRITE|FILE_MAP_READ, |
| 0, 0, sizeof(console_info)); |
| |
| memcpy(ptrView, &console_info, sizeof(console_info)); |
| |
| UnmapViewOfFile(ptrView); |
| |
| /* Map the memory into owner process */ |
| |
| DuplicateHandle(GetCurrentProcess(), hSection, hProcess, &hDupSection, |
| 0, FALSE, DUPLICATE_SAME_ACCESS); |
| |
| /* Send console window the "update" message */ |
| |
| SendMessage(console_info.Hwnd, WM_SETCONSOLEINFO, (WPARAM)hDupSection, 0); |
| |
| CloseHandle(hSection); |
| CloseHandle(hProcess); |
| } |
| |
| static int _set_console_infoex(void) |
| { |
| if (!pSetConsoleScreenBufferInfoEx(pdc_con_out, &console_infoex)) |
| return ERR; |
| |
| return OK; |
| } |
| |
| static int _set_colors(void) |
| { |
| SetConsoleTextAttribute(pdc_con_out, 7); |
| _reset_old_colors(); |
| |
| if (pSetConsoleScreenBufferInfoEx) |
| return _set_console_infoex(); |
| else |
| { |
| _set_console_info(); |
| return OK; |
| } |
| } |
| |
| /* One-time initialization for console_info -- color table and font info |
| from the registry; other values from functions. */ |
| |
| static void _init_console_info(void) |
| { |
| DWORD scrnmode, len; |
| HKEY reghnd; |
| int i; |
| |
| console_info.Hwnd = _find_console_handle(); |
| console_info.Length = sizeof(console_info); |
| |
| GetConsoleMode(pdc_con_in, &scrnmode); |
| console_info.QuickEdit = !!(scrnmode & 0x0040); |
| console_info.InsertMode = !!(scrnmode & 0x0020); |
| |
| console_info.FullScreen = FALSE; |
| console_info.AutoPosition = 0x10000; |
| console_info.ScreenColors = SP->orig_back << 4 | SP->orig_fore; |
| console_info.PopupColors = 0xf5; |
| |
| console_info.HistoryNoDup = FALSE; |
| console_info.HistoryBufferSize = 50; |
| console_info.NumberOfHistoryBuffers = 4; |
| |
| console_info.CodePage = GetConsoleOutputCP(); |
| |
| RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Console"), 0, |
| KEY_QUERY_VALUE, ®hnd); |
| |
| len = sizeof(DWORD); |
| |
| /* Default color table */ |
| |
| for (i = 0; i < 16; i++) |
| { |
| char tname[13]; |
| |
| sprintf(tname, "ColorTable%02d", i); |
| RegQueryValueExA(reghnd, tname, NULL, NULL, |
| (LPBYTE)(&(console_info.ColorTable[i])), &len); |
| } |
| |
| /* Font info */ |
| |
| RegQueryValueEx(reghnd, TEXT("FontSize"), NULL, NULL, |
| (LPBYTE)(&console_info.FontSize), &len); |
| RegQueryValueEx(reghnd, TEXT("FontFamily"), NULL, NULL, |
| (LPBYTE)(&console_info.FontFamily), &len); |
| RegQueryValueEx(reghnd, TEXT("FontWeight"), NULL, NULL, |
| (LPBYTE)(&console_info.FontWeight), &len); |
| |
| len = sizeof(WCHAR) * 32; |
| RegQueryValueExW(reghnd, L"FaceName", NULL, NULL, |
| (LPBYTE)(console_info.FaceName), &len); |
| |
| RegCloseKey(reghnd); |
| } |
| |
| static int _init_console_infoex(void) |
| { |
| console_infoex.cbSize = sizeof(console_infoex); |
| |
| if (!pGetConsoleScreenBufferInfoEx(pdc_con_out, &console_infoex)) |
| return ERR; |
| |
| console_infoex.srWindow.Right++; |
| console_infoex.srWindow.Bottom++; |
| |
| return OK; |
| } |
| |
| static COLORREF *_get_colors(void) |
| { |
| if (pGetConsoleScreenBufferInfoEx) |
| { |
| int status = OK; |
| if (!console_infoex.cbSize) |
| status = _init_console_infoex(); |
| return (status == ERR) ? NULL : |
| (COLORREF *)(&(console_infoex.ColorTable)); |
| } |
| else |
| { |
| if (!console_info.Hwnd) |
| _init_console_info(); |
| return (COLORREF *)(&(console_info.ColorTable)); |
| } |
| } |
| |
| /* restore the original console buffer in the event of a crash */ |
| |
| static LONG WINAPI _restore_console(LPEXCEPTION_POINTERS ep) |
| { |
| PDC_scr_close(); |
| |
| return EXCEPTION_CONTINUE_SEARCH; |
| } |
| |
| /* restore the original console buffer on Ctrl+Break (or Ctrl+C, |
| if it gets re-enabled) */ |
| |
| static BOOL WINAPI _ctrl_break(DWORD dwCtrlType) |
| { |
| if (dwCtrlType == CTRL_BREAK_EVENT || dwCtrlType == CTRL_C_EVENT) |
| PDC_scr_close(); |
| |
| return FALSE; |
| } |
| |
| /* close the physical screen -- may restore the screen to its state |
| before PDC_scr_open(); miscellaneous cleanup */ |
| |
| void PDC_scr_close(void) |
| { |
| PDC_LOG(("PDC_scr_close() - called\n")); |
| |
| if (SP->visibility != 1) |
| curs_set(1); |
| |
| PDC_reset_shell_mode(); |
| |
| /* Position cursor to the bottom left of the screen. */ |
| |
| if (SP->_restore == PDC_RESTORE_NONE) |
| { |
| SMALL_RECT win; |
| |
| win.Left = orig_scr.srWindow.Left; |
| win.Right = orig_scr.srWindow.Right; |
| win.Top = 0; |
| win.Bottom = orig_scr.srWindow.Bottom - orig_scr.srWindow.Top; |
| SetConsoleWindowInfo(pdc_con_out, TRUE, &win); |
| PDC_gotoyx(win.Bottom, 0); |
| } |
| } |
| |
| void PDC_scr_free(void) |
| { |
| if (pdc_con_out != std_con_out) |
| { |
| CloseHandle(pdc_con_out); |
| pdc_con_out = std_con_out; |
| } |
| |
| SetUnhandledExceptionFilter(xcpt_filter); |
| SetConsoleCtrlHandler(_ctrl_break, FALSE); |
| } |
| |
| /* open the physical screen -- miscellaneous initialization, may save |
| the existing screen for later restoration */ |
| |
| int PDC_scr_open(void) |
| { |
| const char *str; |
| CONSOLE_SCREEN_BUFFER_INFO csbi; |
| HMODULE h_kernel; |
| BOOL result; |
| int i; |
| |
| PDC_LOG(("PDC_scr_open() - called\n")); |
| |
| for (i = 0; i < 16; i++) |
| { |
| pdc_curstoreal[realtocurs[i]] = i; |
| pdc_curstoansi[ansitocurs[i]] = i; |
| } |
| _reset_old_colors(); |
| |
| std_con_out = |
| pdc_con_out = GetStdHandle(STD_OUTPUT_HANDLE); |
| pdc_con_in = GetStdHandle(STD_INPUT_HANDLE); |
| |
| if (GetFileType(pdc_con_in) != FILE_TYPE_CHAR) |
| { |
| fprintf(stderr, "\nRedirection is not supported.\n"); |
| exit(1); |
| } |
| |
| is_nt = !(GetVersion() & 0x80000000); |
| |
| pdc_wt = !!getenv("WT_SESSION"); |
| str = pdc_wt ? NULL : getenv("ConEmuANSI"); |
| pdc_conemu = !!str; |
| pdc_ansi = pdc_wt ? TRUE : pdc_conemu ? !strcmp(str, "ON") : FALSE; |
| |
| GetConsoleScreenBufferInfo(pdc_con_out, &csbi); |
| GetConsoleScreenBufferInfo(pdc_con_out, &orig_scr); |
| GetConsoleMode(pdc_con_in, &old_console_mode); |
| |
| /* preserve QuickEdit Mode setting for use in PDC_mouse_set() when |
| the mouse is not enabled -- other console input settings are |
| cleared */ |
| |
| pdc_quick_edit = old_console_mode & 0x0040; |
| |
| SP->mouse_wait = PDC_CLICK_PERIOD; |
| SP->audible = TRUE; |
| |
| SP->termattrs = A_COLOR | A_REVERSE; |
| if (pdc_ansi) |
| SP->termattrs |= A_UNDERLINE | A_ITALIC; |
| |
| SP->orig_fore = csbi.wAttributes & 0x0f; |
| SP->orig_back = (csbi.wAttributes & 0xf0) >> 4; |
| |
| SP->orig_attr = TRUE; |
| |
| SP->_restore = PDC_RESTORE_NONE; |
| |
| if ((str = getenv("PDC_RESTORE_SCREEN")) == NULL || *str != '0') |
| { |
| /* Create a new console buffer */ |
| |
| pdc_con_out = |
| CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, |
| FILE_SHARE_READ | FILE_SHARE_WRITE, |
| NULL, CONSOLE_TEXTMODE_BUFFER, NULL); |
| |
| if (pdc_con_out == INVALID_HANDLE_VALUE) |
| { |
| PDC_LOG(("PDC_scr_open() - screen buffer failure\n")); |
| |
| pdc_con_out = std_con_out; |
| } |
| else |
| SP->_restore = PDC_RESTORE_BUFFER; |
| } |
| |
| xcpt_filter = SetUnhandledExceptionFilter(_restore_console); |
| SetConsoleCtrlHandler(_ctrl_break, TRUE); |
| |
| SP->_preserve = (getenv("PDC_PRESERVE_SCREEN") != NULL); |
| |
| /* ENABLE_LVB_GRID_WORLDWIDE */ |
| result = SetConsoleMode(pdc_con_out, 0x0010); |
| if (result) |
| SP->termattrs |= A_UNDERLINE | A_LEFT | A_RIGHT; |
| |
| PDC_reset_prog_mode(); |
| |
| SP->mono = FALSE; |
| |
| h_kernel = GetModuleHandleA("kernel32.dll"); |
| pGetConsoleScreenBufferInfoEx = |
| (GetConsoleScreenBufferInfoExFn)GetProcAddress(h_kernel, |
| "GetConsoleScreenBufferInfoEx"); |
| pSetConsoleScreenBufferInfoEx = |
| (SetConsoleScreenBufferInfoExFn)GetProcAddress(h_kernel, |
| "SetConsoleScreenBufferInfoEx"); |
| |
| return OK; |
| } |
| |
| /* Calls SetConsoleWindowInfo with the given parameters, but fits them |
| if a scoll bar shrinks the maximum possible value. The rectangle |
| must at least fit in a half-sized window. */ |
| |
| static BOOL _fit_console_window(HANDLE con_out, CONST SMALL_RECT *rect) |
| { |
| SMALL_RECT run; |
| SHORT mx, my; |
| |
| if (SetConsoleWindowInfo(con_out, TRUE, rect)) |
| return TRUE; |
| |
| run = *rect; |
| run.Right /= 2; |
| run.Bottom /= 2; |
| |
| mx = run.Right; |
| my = run.Bottom; |
| |
| if (!SetConsoleWindowInfo(con_out, TRUE, &run)) |
| return FALSE; |
| |
| for (run.Right = rect->Right; run.Right >= mx; run.Right--) |
| if (SetConsoleWindowInfo(con_out, TRUE, &run)) |
| break; |
| |
| if (run.Right < mx) |
| return FALSE; |
| |
| for (run.Bottom = rect->Bottom; run.Bottom >= my; run.Bottom--) |
| if (SetConsoleWindowInfo(con_out, TRUE, &run)) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| /* the core of resize_term() */ |
| |
| int PDC_resize_screen(int nlines, int ncols) |
| { |
| SMALL_RECT rect; |
| COORD size, max; |
| |
| bool prog_resize = nlines || ncols; |
| |
| if (!prog_resize) |
| { |
| nlines = PDC_get_rows(); |
| ncols = PDC_get_columns(); |
| } |
| |
| if (nlines < 2 || ncols < 2) |
| return ERR; |
| |
| max = GetLargestConsoleWindowSize(pdc_con_out); |
| |
| rect.Left = rect.Top = 0; |
| rect.Right = ncols - 1; |
| |
| if (rect.Right > max.X) |
| rect.Right = max.X; |
| |
| rect.Bottom = nlines - 1; |
| |
| if (rect.Bottom > max.Y) |
| rect.Bottom = max.Y; |
| |
| size.X = rect.Right + 1; |
| size.Y = rect.Bottom + 1; |
| |
| _fit_console_window(pdc_con_out, &rect); |
| SetConsoleScreenBufferSize(pdc_con_out, size); |
| |
| if (prog_resize) |
| { |
| _fit_console_window(pdc_con_out, &rect); |
| SetConsoleScreenBufferSize(pdc_con_out, size); |
| } |
| SetConsoleActiveScreenBuffer(pdc_con_out); |
| |
| PDC_flushinp(); |
| |
| return OK; |
| } |
| |
| void PDC_reset_prog_mode(void) |
| { |
| PDC_LOG(("PDC_reset_prog_mode() - called.\n")); |
| |
| if (pdc_con_out != std_con_out) |
| SetConsoleActiveScreenBuffer(pdc_con_out); |
| else if (is_nt) |
| { |
| COORD bufsize; |
| SMALL_RECT rect; |
| |
| bufsize.X = orig_scr.srWindow.Right - orig_scr.srWindow.Left + 1; |
| bufsize.Y = orig_scr.srWindow.Bottom - orig_scr.srWindow.Top + 1; |
| |
| rect.Top = rect.Left = 0; |
| rect.Bottom = bufsize.Y - 1; |
| rect.Right = bufsize.X - 1; |
| |
| SetConsoleScreenBufferSize(pdc_con_out, bufsize); |
| SetConsoleWindowInfo(pdc_con_out, TRUE, &rect); |
| SetConsoleScreenBufferSize(pdc_con_out, bufsize); |
| SetConsoleActiveScreenBuffer(pdc_con_out); |
| } |
| |
| PDC_mouse_set(); |
| } |
| |
| void PDC_reset_shell_mode(void) |
| { |
| PDC_LOG(("PDC_reset_shell_mode() - called.\n")); |
| |
| if (pdc_con_out != std_con_out) |
| SetConsoleActiveScreenBuffer(std_con_out); |
| else if (is_nt) |
| { |
| SetConsoleScreenBufferSize(pdc_con_out, orig_scr.dwSize); |
| SetConsoleWindowInfo(pdc_con_out, TRUE, &orig_scr.srWindow); |
| SetConsoleScreenBufferSize(pdc_con_out, orig_scr.dwSize); |
| SetConsoleWindowInfo(pdc_con_out, TRUE, &orig_scr.srWindow); |
| SetConsoleActiveScreenBuffer(pdc_con_out); |
| } |
| |
| SetConsoleMode(pdc_con_in, old_console_mode | 0x0080); |
| } |
| |
| void PDC_restore_screen_mode(int i) |
| { |
| } |
| |
| void PDC_save_screen_mode(int i) |
| { |
| } |
| |
| bool PDC_can_change_color(void) |
| { |
| return is_nt; |
| } |
| |
| int PDC_color_content(short color, short *red, short *green, short *blue) |
| { |
| if (color < 16 && !(pdc_conemu || pdc_wt)) |
| { |
| COLORREF *color_table = _get_colors(); |
| |
| if (color_table) |
| { |
| DWORD col = color_table[pdc_curstoreal[color]]; |
| |
| *red = DIVROUND(GetRValue(col) * 1000, 255); |
| *green = DIVROUND(GetGValue(col) * 1000, 255); |
| *blue = DIVROUND(GetBValue(col) * 1000, 255); |
| } |
| else |
| return ERR; |
| } |
| else |
| { |
| if (!pdc_color[color].mapped) |
| { |
| *red = *green = *blue = -1; |
| return ERR; |
| } |
| |
| *red = pdc_color[color].r; |
| *green = pdc_color[color].g; |
| *blue = pdc_color[color].b; |
| } |
| |
| return OK; |
| } |
| |
| int PDC_init_color(short color, short red, short green, short blue) |
| { |
| if (red == -1 && green == -1 && blue == -1) |
| { |
| pdc_color[color].mapped = FALSE; |
| return OK; |
| } |
| |
| if (color < 16 && !(pdc_conemu || pdc_wt)) |
| { |
| COLORREF *color_table = _get_colors(); |
| |
| if (color_table) |
| { |
| color_table[pdc_curstoreal[color]] = |
| RGB(DIVROUND(red * 255, 1000), |
| DIVROUND(green * 255, 1000), |
| DIVROUND(blue * 255, 1000)); |
| |
| return _set_colors(); |
| } |
| |
| return ERR; |
| } |
| else |
| { |
| pdc_color[color].r = red; |
| pdc_color[color].g = green; |
| pdc_color[color].b = blue; |
| pdc_color[color].mapped = TRUE; |
| } |
| |
| return OK; |
| } |