From 817cae451880a90f6186dc7eaf1dc0ac20660992 Mon Sep 17 00:00:00 2001 From: "nethack.allison" Date: Sun, 3 Sep 2006 04:17:09 +0000 Subject: [PATCH] adjustment of win32 console colours (trunk only) Allow config file entries to adjust win32 console colours. The following entries in a config file are examples: OPTIONS=palette:black-0-0-0 OPTIONS=palette:red-210-0-0 OPTIONS=palette:green-80-200-0 OPTIONS=palette:brown-180-100-0 OPTIONS=palette:blue-0-0-200 OPTIONS=palette:magenta-128-0-128 OPTIONS=palette:cyan-50-180-180 OPTIONS=palette:gray-192-192-192 OPTIONS=palette:dark gray-100-100-100 OPTIONS=palette:orange-255-128-0 OPTIONS=palette:bright green-0-255-0 OPTIONS=palette:yellow-255-255-0 OPTIONS=palette:bright blue-100-100-240 OPTIONS=palette:bright magenta-255-0-255 OPTIONS=palette:bright cyan-0-255-255 OPTIONS=palette:white-255-255-255 This uses an undocumented way to adjust the console colours in a win32 console application. The method and code snippet used comes from www.catch22.net by James Brown. This page: http://www.catch22.net/about.asp states the following: "you do not have to pay anything to use the software, and there are no licencing terms for any sourcecode that you may download from this site. This means you can freely use any sourcecode or portions of code in your applications, whether they be free software or professional, retail products." --- include/ntconf.h | 4 + src/options.c | 25 ++- sys/winnt/defaults.nh | 21 +++ sys/winnt/nttty.c | 366 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 410 insertions(+), 6 deletions(-) diff --git a/include/ntconf.h b/include/ntconf.h index faeb6aad8..8323af597 100644 --- a/include/ntconf.h +++ b/include/ntconf.h @@ -26,6 +26,7 @@ #define USER_SOUNDS #ifdef WIN32CON +#define CHANGE_COLOR /* allow palette changes in win32 console */ #define SELECTSAVED /* Provide menu of saved games to choose from at start */ #endif @@ -231,6 +232,9 @@ extern int FDECL(set_win32_option, (const char *, const char *)); #define RIGHTBUTTON RIGHTMOST_BUTTON_PRESSED #define MIDBUTTON FROM_LEFT_2ND_BUTTON_PRESSED #define MOUSEMASK (LEFTBUTTON | RIGHTBUTTON | MIDBUTTON) +#ifdef CHANGE_COLOR +extern int FDECL(alternative_palette, (char *)); +#endif #endif /* WIN32CON */ #endif /* NTCONF_H */ diff --git a/src/options.c b/src/options.c index cddb16faf..21ac55689 100644 --- a/src/options.c +++ b/src/options.c @@ -311,8 +311,14 @@ static struct Comp_Opt { "packorder", "the inventory order of the items in your pack", MAXOCLASSES, SET_IN_GAME }, #ifdef CHANGE_COLOR - { "palette", "palette (00c/880/-fff is blue/yellow/reverse white)", + { "palette", +# ifndef WIN32CON + "palette (00c/880/-fff is blue/yellow/reverse white)", 15 , SET_IN_GAME }, +# else + "palette (adjust an RGB color in palette (color-R-G-B)", + 15 , SET_IN_FILE }, +# endif # if defined(MAC) { "hicolor", "same as palette, only order is reversed", 15, SET_IN_FILE }, @@ -1445,7 +1451,9 @@ boolean tinitial, tfrom_file; ) { int color_number, color_incr; +#ifndef WIN32CON if (duplicate) complain_about_duplicate(opts,1); +#endif # ifdef MAC if (match_optname(opts, "hicolor", 3, TRUE)) { if (negated) { @@ -1466,6 +1474,10 @@ boolean tinitial, tfrom_file; } # endif if ((op = string_for_opt(opts, FALSE)) != (char *)0) { +# ifdef WIN32CON + if (!alternative_palette(op)) + badoption(opts); +# else char *pt = op; int cnt, tmp, reverse; long rgb; @@ -1481,21 +1493,21 @@ boolean tinitial, tfrom_file; } while (cnt-- > 0) { if (*pt && *pt != '/') { -# ifdef AMIGA +# ifdef AMIGA rgb <<= 4; -# else +# else rgb <<= 8; -# endif +# endif tmp = *(pt++); if (isalpha(tmp)) { tmp = (tmp + 9) & 0xf; /* Assumes ASCII... */ } else { tmp &= 0xf; /* Digits in ASCII too... */ } -# ifndef AMIGA +# ifndef AMIGA /* Add an extra so we fill f -> ff and 0 -> 00 */ rgb += tmp << 4; -# endif +# endif rgb += tmp; } } @@ -1505,6 +1517,7 @@ boolean tinitial, tfrom_file; change_color(color_number, rgb, reverse); color_number += color_incr; } +# endif /* !WIN32CON */ } if (!initial) { need_redraw = TRUE; diff --git a/sys/winnt/defaults.nh b/sys/winnt/defaults.nh index b0cfe015c..be1d53ef7 100644 --- a/sys/winnt/defaults.nh +++ b/sys/winnt/defaults.nh @@ -125,6 +125,27 @@ OPTIONS=hilite_pet,!toptenwin #OPTIONS=subkeyvalue:184/91 #OPTIONS=subkeyvalue:188/124 +# +# Some versions of Windows allow you to adjust the win32 console port +# colors using R-G-B settings. +# +#OPTIONS=palette:black-0-0-0 +#OPTIONS=palette:red-210-0-0 +#OPTIONS=palette:green-80-200-0 +#OPTIONS=palette:brown-180-100-0 +#OPTIONS=palette:blue-0-0-200 +#OPTIONS=palette:magenta-128-0-128 +#OPTIONS=palette:cyan-50-180-180 +#OPTIONS=palette:gray-192-192-192 +#OPTIONS=palette:dark gray-100-100-100 +#OPTIONS=palette:orange-255-128-0 +#OPTIONS=palette:bright green-0-255-0 +#OPTIONS=palette:yellow-255-255-0 +#OPTIONS=palette:bright blue-100-100-240 +#OPTIONS=palette:bright magenta-255-0-255 +#OPTIONS=palette:bright cyan-0-255-255 +#OPTIONS=palette:white-255-255-255 + # # *** CHARACTER GRAPHICS *** # diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index fc4be4451..9f7885d1f 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -37,6 +37,7 @@ int FDECL(process_keystroke, (INPUT_RECORD *, boolean *, * ReadConsoleInput * WriteConsoleOutputCharacter * FillConsoleOutputAttribute + * GetConsoleOutputCP */ /* Win32 Console handles for input and output */ @@ -74,6 +75,26 @@ typedef int (__stdcall * PROCESS_KEYSTROKE)( int ); +#ifdef CHANGE_COLOR +static void NDECL(adjust_palette); +static int FDECL(match_color_name, (const char *)); +static boolean altered_palette; +static COLORREF UserDefinedColors[CLR_MAX]; +static COLORREF NetHackColors[CLR_MAX] = { + 0x00000000,0x00c80000,0x0000c850,0x00b4b432, + 0x000000d2,0x00800080,0x000064b4,0x00c0c0c0, + 0x00646464,0x00f06464,0x0000ff00,0x00ffff00, + 0x000000ff,0x00ff00ff,0x0000ffff,0x00ffffff + }; +static COLORREF DefaultColors[CLR_MAX] = { + 0x00000000, 0x00800000, 0x00008000, 0x00808000, + 0x00000080, 0x00800080, 0x00008080, 0x00c0c0c0, + 0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00, + 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff + }; + +#endif + typedef int (__stdcall * NHKBHIT)( HANDLE, INPUT_RECORD * @@ -166,6 +187,9 @@ const char *s; void setftty() { +#ifdef CHANGE_COLOR + if (altered_palette) adjust_palette(); +#endif start_screen(); } @@ -973,4 +997,346 @@ synch_cursor() { really_move_cursor(); } + +# ifdef CHANGE_COLOR +void tty_change_color(color_number, rgb, reverse) +int color_number, reverse; +long rgb; +{ + /* Map NetHack color index to NT Console palette index */ + int idx, win32_color_number[] = { + 0, /* CLR_BLACK 0 */ + 4, /* CLR_RED 1 */ + 2, /* CLR_GREEN 2 */ + 6, /* CLR_BROWN 3 */ + 1, /* CLR_BLUE 4 */ + 5, /* CLR_MAGENTA 5 */ + 3, /* CLR_CYAN 6 */ + 7, /* CLR_GRAY 7 */ + 8, /* NO_COLOR 8 */ + 12, /* CLR_ORANGE 9 */ + 10, /* CLR_BRIGHT_GREEN 10 */ + 14, /* CLR_YELLOW 11 */ + 9, /* CLR_BRIGHT_BLUE 12 */ + 13, /* CLR_BRIGHT_MAGENTA 13 */ + 11, /* CLR_BRIGHT_CYAN 14 */ + 15 /* CLR_WHITE 15 */ + }; + int k; + if (color_number < 0) { /* indicates OPTIONS=palette with no value */ + /* copy the NetHack palette into UserDefinedColors */ + for (k=0; k < CLR_MAX; k++) + UserDefinedColors[k] = NetHackColors[k]; + return; + } else if (color_number >= 0 && color_number < CLR_MAX) { + idx = win32_color_number[color_number]; + UserDefinedColors[idx] = rgb; + } + altered_palette = TRUE; +} + +char *tty_get_color_string() +{ + return ""; +} + +int +match_color_name(c) +const char *c; +{ + const struct others { + int idx; + const char *colorname; + } othernames[] = { + {CLR_MAGENTA, "purple"}, + {CLR_BRIGHT_MAGENTA, "bright purple"}, + {NO_COLOR, "dark gray"}, + {NO_COLOR, "dark grey"}, + {CLR_GRAY, "grey"}, + }; + + int cnt; + for (cnt = 0; cnt < CLR_MAX; ++cnt) { + if (!strcmpi(c, c_obj_colors[cnt])) + return cnt; + } + for (cnt = 0; cnt < SIZE(othernames); ++cnt) { + if (!strcmpi(c, othernames[cnt].colorname)) + return othernames[cnt].idx; + } + return -1; +} + +/* + * Returns 0 if badoption syntax + */ +int +alternative_palette(op) +char *op; +{ + /* + * palette:color-R-G-B + * OPTIONS=palette:green-4-3-1, palette:0-0-0-0 + */ + int fieldcnt, color_number, rgb, red, green, blue; + char *fields[4], *cp; + + if (!op) { + change_color(-1,0,0); /* indicates palette option with + no value meaning "load an entire + hard-coded NetHack palette." */ + return 1; + } + + cp = fields[0] = op; + for (fieldcnt = 1; fieldcnt < 4; ++fieldcnt) { + cp = index(cp, '-'); + if (!cp) return 0; + fields[fieldcnt] = cp; + cp++; + } + for (fieldcnt = 1; fieldcnt < 4; ++fieldcnt) { + *(fields[fieldcnt]) = '\0'; + ++fields[fieldcnt]; + } + rgb = 0; + for (fieldcnt = 0; fieldcnt < 4; ++fieldcnt) { + if (fieldcnt == 0 && isalpha(*(fields[0]))) { + color_number = match_color_name(fields[0]); + if (color_number == -1) return 0; + } else { + int dcount = 0, cval = 0; + cp = fields[fieldcnt]; + if (*cp == '\\' && index("0123456789xXoO", cp[1])) { + const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF"; + + cp++; + if (*cp == 'x' || *cp == 'X') + for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++) + cval = (int)((cval * 16) + (dp - hex) / 2); + else if (*cp == 'o' || *cp == 'O') + for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++) + cval = (cval * 8) + (*cp - '0'); + else + return 0; + } else { + for (; *cp && (index("0123456789",*cp)) && (dcount++ < 3); cp++) + cval = (cval * 10) + (*cp - '0'); + } + switch(fieldcnt) { + case 0: + color_number = cval; + break; + case 1: + red = cval; + break; + case 2: + green = cval; + break; + case 3: + blue = cval; + break; + } + } + } + rgb = RGB(red,green,blue); + if (color_number >= 0 && color_number < CLR_MAX) + change_color(color_number, rgb, 0); + return 1; +} + +/* + * This uses an undocumented method to set console attributes + * at runtime including console palette + * + * VOID WINAPI SetConsolePalette(COLORREF palette[16]) + * + * Author: James Brown at www.catch22.net + * + * Set palette of current console. + * Palette should be of the form: + * + * COLORREF DefaultColors[CLR_MAX] = + * { + * 0x00000000, 0x00800000, 0x00008000, 0x00808000, + * 0x00000080, 0x00800080, 0x00008080, 0x00c0c0c0, + * 0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00, + * 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff + * }; + */ + +#pragma pack(push, 1) + +/* + * Structure to send console via WM_SETCONSOLEINFO + */ +typedef struct _CONSOLE_INFO +{ + 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; + +#pragma pack(pop) + +BOOL SetConsoleInfo(HWND hwndConsole, CONSOLE_INFO *pci); +static void GetConsoleSizeInfo(CONSOLE_INFO *pci); +VOID WINAPI SetConsolePalette(COLORREF crPalette[16]); + +void +adjust_palette(VOID_ARGS) +{ + SetConsolePalette(UserDefinedColors); + altered_palette = 0; +} + +/* +/* only in Win2k+ (use FindWindow for NT4) */ +HWND WINAPI GetConsoleWindow(); + +/* Undocumented console message */ +#define WM_SETCONSOLEINFO (WM_USER+201) + + +VOID WINAPI SetConsolePalette(COLORREF palette[16]) +{ + CONSOLE_INFO ci = { sizeof(ci) }; + int i; + HWND hwndConsole = GetConsoleWindow(); + + /* get current size/position settings rather than using defaults.. */ + GetConsoleSizeInfo(&ci); + + /* set these to zero to keep current settings */ + ci.FontSize.X = 0; /* def = 8 */ + ci.FontSize.Y = 0; /* def = 12 */ + ci.FontFamily = 0; /* def = 0x30 = FF_MODERN|FIXED_PITCH */ + ci.FontWeight = 0; /* 0x400; */ + /* lstrcpyW(ci.FaceName, L"Terminal"); */ + ci.FaceName[0] = L'\0'; + + ci.CursorSize = 25; + ci.FullScreen = FALSE; + ci.QuickEdit = TRUE; + ci.AutoPosition = 0x10000; + ci.InsertMode = TRUE; + ci.ScreenColors = MAKEWORD(0x7, 0x0); + ci.PopupColors = MAKEWORD(0x5, 0xf); + + ci.HistoryNoDup = FALSE; + ci.HistoryBufferSize = 50; + ci.NumberOfHistoryBuffers = 4; + + // colour table + for(i = 0; i < 16; i++) + ci.ColorTable[i] = palette[i]; + + ci.CodePage = GetConsoleOutputCP(); + ci.Hwnd = hwndConsole; + + lstrcpyW(ci.ConsoleTitle, L""); + + SetConsoleInfo(hwndConsole, &ci); +} + +/* + * 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 + */ +BOOL SetConsoleInfo(HWND hwndConsole, CONSOLE_INFO *pci) +{ + DWORD dwConsoleOwnerPid; + HANDLE hProcess; + HANDLE hSection, hDupSection; + PVOID ptrView = 0; + HANDLE hThread; + + /* + * Open the process which "owns" the console + */ + GetWindowThreadProcessId(hwndConsole, &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, pci->Length, 0); + + /* + * Copy our console structure into the section-object + */ + ptrView = MapViewOfFile(hSection, FILE_MAP_WRITE|FILE_MAP_READ, 0, 0, pci->Length); + memcpy(ptrView, pci, pci->Length); + 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(hwndConsole, WM_SETCONSOLEINFO, (WPARAM)hDupSection, 0); + + /* + * clean up + */ + hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)CloseHandle, + hDupSection, 0, 0); + + CloseHandle(hThread); + CloseHandle(hSection); + CloseHandle(hProcess); + + return TRUE; +} + +/* + * Fill the CONSOLE_INFO structure with information + * about the current console window + */ +static void GetConsoleSizeInfo(CONSOLE_INFO *pci) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + + HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE); + + GetConsoleScreenBufferInfo(hConsoleOut, &csbi); + + pci->ScreenBufferSize = csbi.dwSize; + pci->WindowSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1; + pci->WindowSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + pci->WindowPosX = csbi.srWindow.Left; + pci->WindowPosY = csbi.srWindow.Top; +} +# endif /*CHANGE_COLOR*/ #endif /* WIN32CON */ -- 2.40.0