/* Public Domain Curses */ #include "pdcwin.h" RCSID("$Id: pdcscrn.c,v 1.92 2008/07/20 20:12:04 wmcbrine Exp $") #ifdef CHTYPE_LONG # define PDC_OFFSET 32 #else # define PDC_OFFSET 8 #endif /* COLOR_PAIR to attribute encoding table. */ unsigned char *pdc_atrtab = (unsigned char *)NULL; HANDLE pdc_con_out = INVALID_HANDLE_VALUE; HANDLE pdc_con_in = INVALID_HANDLE_VALUE; DWORD pdc_quick_edit; static short curstoreal[16], 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 }; enum { PDC_RESTORE_NONE, PDC_RESTORE_BUFFER, PDC_RESTORE_WINDOW }; /* 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; static CONSOLE_SCREEN_BUFFER_INFO orig_scr; static CHAR_INFO *ci_save = NULL; static DWORD old_console_mode = 0; static bool is_nt; 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); } /* 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); } /* close the physical screen -- may restore the screen to its state before PDC_scr_open(); miscellaneous cleanup */ void PDC_scr_close(void) { COORD origin; SMALL_RECT rect; PDC_LOG(("PDC_scr_close() - called\n")); PDC_reset_shell_mode(); if (SP->_restore != PDC_RESTORE_NONE) { if (SP->_restore == PDC_RESTORE_WINDOW) { rect.Top = orig_scr.srWindow.Top; rect.Left = orig_scr.srWindow.Left; rect.Bottom = orig_scr.srWindow.Bottom; rect.Right = orig_scr.srWindow.Right; } else /* PDC_RESTORE_BUFFER */ { rect.Top = rect.Left = 0; rect.Bottom = orig_scr.dwSize.Y - 1; rect.Right = orig_scr.dwSize.X - 1; } origin.X = origin.Y = 0; if (!WriteConsoleOutput(pdc_con_out, ci_save, orig_scr.dwSize, origin, &rect)) return; } if (SP->visibility != 1) curs_set(1); /* Position cursor to the bottom left of the screen. */ PDC_gotoyx(PDC_get_buffer_rows() - 2, 0); } void PDC_scr_free(void) { if (SP) free(SP); if (pdc_atrtab) free(pdc_atrtab); pdc_atrtab = (unsigned char *)NULL; } /* open the physical screen -- allocate SP, miscellaneous intialization, and may save the existing screen for later restoration */ int PDC_scr_open(int argc, char **argv) { COORD bufsize, origin; SMALL_RECT rect; const char *str; CONSOLE_SCREEN_BUFFER_INFO csbi; int i; PDC_LOG(("PDC_scr_open() - called\n")); SP = calloc(1, sizeof(SCREEN)); pdc_atrtab = calloc(PDC_COLOR_PAIRS * PDC_OFFSET, 1); if (!SP || !pdc_atrtab) return ERR; for (i = 0; i < 16; i++) curstoreal[realtocurs[i]] = i; 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); 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->lines = (str = getenv("LINES")) ? atoi(str) : PDC_get_rows(); SP->cols = (str = getenv("COLS")) ? atoi(str) : PDC_get_columns(); SP->mouse_wait = PDC_CLICK_PERIOD; SP->audible = TRUE; if (SP->lines < 2 || SP->lines > csbi.dwMaximumWindowSize.Y) { fprintf(stderr, "LINES value must be >= 2 and <= %d: got %d\n", csbi.dwMaximumWindowSize.Y, SP->lines); return ERR; } if (SP->cols < 2 || SP->cols > csbi.dwMaximumWindowSize.X) { fprintf(stderr, "COLS value must be >= 2 and <= %d: got %d\n", csbi.dwMaximumWindowSize.X, SP->cols); return ERR; } SP->orig_fore = csbi.wAttributes & 0x0f; SP->orig_back = (csbi.wAttributes & 0xf0) >> 4; SP->orig_attr = TRUE; SP->_restore = PDC_RESTORE_NONE; if (getenv("PDC_RESTORE_SCREEN")) { /* Attempt to save the complete console buffer */ ci_save = malloc(orig_scr.dwSize.X * orig_scr.dwSize.Y * sizeof(CHAR_INFO)); if (!ci_save) { PDC_LOG(("PDC_scr_open() - malloc failure (1)\n")); return ERR; } bufsize.X = orig_scr.dwSize.X; bufsize.Y = orig_scr.dwSize.Y; origin.X = origin.Y = 0; rect.Top = rect.Left = 0; rect.Bottom = orig_scr.dwSize.Y - 1; rect.Right = orig_scr.dwSize.X - 1; if (!ReadConsoleOutput(pdc_con_out, ci_save, bufsize, origin, &rect)) { /* We can't save the complete buffer, so try and save just the displayed window */ free(ci_save); ci_save = NULL; bufsize.X = orig_scr.srWindow.Right - orig_scr.srWindow.Left + 1; bufsize.Y = orig_scr.srWindow.Bottom - orig_scr.srWindow.Top + 1; ci_save = malloc(bufsize.X * bufsize.Y * sizeof(CHAR_INFO)); if (!ci_save) { PDC_LOG(("PDC_scr_open() - malloc failure (2)\n")); return ERR; } origin.X = origin.Y = 0; rect.Top = orig_scr.srWindow.Top; rect.Left = orig_scr.srWindow.Left; rect.Bottom = orig_scr.srWindow.Bottom; rect.Right = orig_scr.srWindow.Right; if (!ReadConsoleOutput(pdc_con_out, ci_save, bufsize, origin, &rect)) { #ifdef PDCDEBUG CHAR LastError[256]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), LastError, 256, NULL); PDC_LOG(("PDC_scr_open() - %s\n", LastError)); #endif free(ci_save); ci_save = NULL; return ERR; } SP->_restore = PDC_RESTORE_WINDOW; } else SP->_restore = PDC_RESTORE_BUFFER; } SP->_preserve = (getenv("PDC_PRESERVE_SCREEN") != NULL); PDC_reset_prog_mode(); SP->mono = FALSE; 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; 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); _fit_console_window(pdc_con_out, &rect); SetConsoleScreenBufferSize(pdc_con_out, size); SetConsoleActiveScreenBuffer(pdc_con_out); return OK; } void PDC_reset_prog_mode(void) { PDC_LOG(("PDC_reset_prog_mode() - called.\n")); 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 (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); } void PDC_restore_screen_mode(int i) { } void PDC_save_screen_mode(int i) { } void PDC_init_pair(short pair, short fg, short bg) { unsigned char att, temp_bg; chtype i; fg = curstoreal[fg]; bg = curstoreal[bg]; for (i = 0; i < PDC_OFFSET; i++) { att = fg | (bg << 4); if (i & (A_REVERSE >> PDC_ATTR_SHIFT)) att = bg | (fg << 4); if (i & (A_UNDERLINE >> PDC_ATTR_SHIFT)) att = 1; if (i & (A_INVIS >> PDC_ATTR_SHIFT)) { temp_bg = att >> 4; att = temp_bg << 4 | temp_bg; } if (i & (A_BOLD >> PDC_ATTR_SHIFT)) att |= 8; if (i & (A_BLINK >> PDC_ATTR_SHIFT)) att |= 128; pdc_atrtab[pair * PDC_OFFSET + i] = att; } } int PDC_pair_content(short pair, short *fg, short *bg) { *fg = realtocurs[pdc_atrtab[pair * PDC_OFFSET] & 0x0F]; *bg = realtocurs[(pdc_atrtab[pair * PDC_OFFSET] & 0xF0) >> 4]; return OK; } bool PDC_can_change_color(void) { return is_nt; } int PDC_color_content(short color, short *red, short *green, short *blue) { DWORD col; if (!console_info.Hwnd) _init_console_info(); col = console_info.ColorTable[curstoreal[color]]; *red = DIVROUND(GetRValue(col) * 1000, 255); *green = DIVROUND(GetGValue(col) * 1000, 255); *blue = DIVROUND(GetBValue(col) * 1000, 255); return OK; } int PDC_init_color(short color, short red, short green, short blue) { if (!console_info.Hwnd) _init_console_info(); console_info.ColorTable[curstoreal[color]] = RGB(DIVROUND(red * 255, 1000), DIVROUND(green * 255, 1000), DIVROUND(blue * 255, 1000)); _set_console_info(); return OK; }