1 /* Public Domain Curses */
5 RCSID("$Id: pdcscrn.c,v 1.92 2008/07/20 20:12:04 wmcbrine Exp $")
13 /* COLOR_PAIR to attribute encoding table. */
15 unsigned char *pdc_atrtab
= (unsigned char *)NULL
;
17 HANDLE pdc_con_out
= INVALID_HANDLE_VALUE
;
18 HANDLE pdc_con_in
= INVALID_HANDLE_VALUE
;
22 static short curstoreal
[16], realtocurs
[16] =
24 COLOR_BLACK
, COLOR_BLUE
, COLOR_GREEN
, COLOR_CYAN
, COLOR_RED
,
25 COLOR_MAGENTA
, COLOR_YELLOW
, COLOR_WHITE
, COLOR_BLACK
+ 8,
26 COLOR_BLUE
+ 8, COLOR_GREEN
+ 8, COLOR_CYAN
+ 8, COLOR_RED
+ 8,
27 COLOR_MAGENTA
+ 8, COLOR_YELLOW
+ 8, COLOR_WHITE
+ 8
30 enum { PDC_RESTORE_NONE
, PDC_RESTORE_BUFFER
, PDC_RESTORE_WINDOW
};
32 /* Struct for storing console registry keys, and for use with the
33 undocumented WM_SETCONSOLEINFO message. Originally by James Brown,
39 COORD ScreenBufferSize
;
58 ULONG HistoryBufferSize
;
59 ULONG NumberOfHistoryBuffers
;
61 COLORREF ColorTable
[16];
66 WCHAR ConsoleTitle
[0x100];
69 static CONSOLE_SCREEN_BUFFER_INFO orig_scr
;
71 static CHAR_INFO
*ci_save
= NULL
;
72 static DWORD old_console_mode
= 0;
76 static HWND
_find_console_handle(void)
78 TCHAR orgtitle
[1024], temptitle
[1024];
81 GetConsoleTitle(orgtitle
, 1024);
83 wsprintf(temptitle
, TEXT("%d/%d"), GetTickCount(), GetCurrentProcessId());
84 SetConsoleTitle(temptitle
);
88 wnd
= FindWindow(NULL
, temptitle
);
90 SetConsoleTitle(orgtitle
);
95 /* Undocumented console message */
97 #define WM_SETCONSOLEINFO (WM_USER + 201)
99 /* Wrapper around WM_SETCONSOLEINFO. We need to create the necessary
100 section (file-mapping) object in the context of the process which
101 owns the console, before posting the message. Originally by JB. */
103 static void _set_console_info(void)
105 CONSOLE_SCREEN_BUFFER_INFO csbi
;
106 CONSOLE_CURSOR_INFO cci
;
107 DWORD dwConsoleOwnerPid
;
109 HANDLE hSection
, hDupSection
;
112 /* Each-time initialization for console_info */
114 GetConsoleCursorInfo(pdc_con_out
, &cci
);
115 console_info
.CursorSize
= cci
.dwSize
;
117 GetConsoleScreenBufferInfo(pdc_con_out
, &csbi
);
118 console_info
.ScreenBufferSize
= csbi
.dwSize
;
120 console_info
.WindowSize
.X
= csbi
.srWindow
.Right
- csbi
.srWindow
.Left
+ 1;
121 console_info
.WindowSize
.Y
= csbi
.srWindow
.Bottom
- csbi
.srWindow
.Top
+ 1;
123 console_info
.WindowPosX
= csbi
.srWindow
.Left
;
124 console_info
.WindowPosY
= csbi
.srWindow
.Top
;
126 /* Open the process which "owns" the console */
128 GetWindowThreadProcessId(console_info
.Hwnd
, &dwConsoleOwnerPid
);
130 hProcess
= OpenProcess(PROCESS_ALL_ACCESS
, FALSE
, dwConsoleOwnerPid
);
132 /* Create a SECTION object backed by page-file, then map a view of
133 this section into the owner process so we can write the contents
134 of the CONSOLE_INFO buffer into it */
136 hSection
= CreateFileMapping(INVALID_HANDLE_VALUE
, 0, PAGE_READWRITE
,
137 0, sizeof(console_info
), 0);
139 /* Copy our console structure into the section-object */
141 ptrView
= MapViewOfFile(hSection
, FILE_MAP_WRITE
|FILE_MAP_READ
,
142 0, 0, sizeof(console_info
));
144 memcpy(ptrView
, &console_info
, sizeof(console_info
));
146 UnmapViewOfFile(ptrView
);
148 /* Map the memory into owner process */
150 DuplicateHandle(GetCurrentProcess(), hSection
, hProcess
, &hDupSection
,
151 0, FALSE
, DUPLICATE_SAME_ACCESS
);
153 /* Send console window the "update" message */
155 SendMessage(console_info
.Hwnd
, WM_SETCONSOLEINFO
, (WPARAM
)hDupSection
, 0);
157 CloseHandle(hSection
);
158 CloseHandle(hProcess
);
161 /* One-time initialization for console_info -- color table and font info
162 from the registry; other values from functions. */
164 static void _init_console_info(void)
170 console_info
.Hwnd
= _find_console_handle();
171 console_info
.Length
= sizeof(console_info
);
173 GetConsoleMode(pdc_con_in
, &scrnmode
);
174 console_info
.QuickEdit
= !!(scrnmode
& 0x0040);
175 console_info
.InsertMode
= !!(scrnmode
& 0x0020);
177 console_info
.FullScreen
= FALSE
;
178 console_info
.AutoPosition
= 0x10000;
179 console_info
.ScreenColors
= SP
->orig_back
<< 4 | SP
->orig_fore
;
180 console_info
.PopupColors
= 0xf5;
182 console_info
.HistoryNoDup
= FALSE
;
183 console_info
.HistoryBufferSize
= 50;
184 console_info
.NumberOfHistoryBuffers
= 4;
186 console_info
.CodePage
= GetConsoleOutputCP();
188 RegOpenKeyEx(HKEY_CURRENT_USER
, TEXT("Console"), 0,
189 KEY_QUERY_VALUE
, ®hnd
);
193 /* Default color table */
195 for (i
= 0; i
< 16; i
++)
199 sprintf(tname
, "ColorTable%02d", i
);
200 RegQueryValueExA(reghnd
, tname
, NULL
, NULL
,
201 (LPBYTE
)(&(console_info
.ColorTable
[i
])), &len
);
206 RegQueryValueEx(reghnd
, TEXT("FontSize"), NULL
, NULL
,
207 (LPBYTE
)(&console_info
.FontSize
), &len
);
208 RegQueryValueEx(reghnd
, TEXT("FontFamily"), NULL
, NULL
,
209 (LPBYTE
)(&console_info
.FontFamily
), &len
);
210 RegQueryValueEx(reghnd
, TEXT("FontWeight"), NULL
, NULL
,
211 (LPBYTE
)(&console_info
.FontWeight
), &len
);
213 len
= sizeof(WCHAR
) * 32;
214 RegQueryValueExW(reghnd
, L
"FaceName", NULL
, NULL
,
215 (LPBYTE
)(console_info
.FaceName
), &len
);
220 /* close the physical screen -- may restore the screen to its state
221 before PDC_scr_open(); miscellaneous cleanup */
223 void PDC_scr_close(void)
228 PDC_LOG(("PDC_scr_close() - called\n"));
230 PDC_reset_shell_mode();
232 if (SP
->_restore
!= PDC_RESTORE_NONE
)
234 if (SP
->_restore
== PDC_RESTORE_WINDOW
)
236 rect
.Top
= orig_scr
.srWindow
.Top
;
237 rect
.Left
= orig_scr
.srWindow
.Left
;
238 rect
.Bottom
= orig_scr
.srWindow
.Bottom
;
239 rect
.Right
= orig_scr
.srWindow
.Right
;
241 else /* PDC_RESTORE_BUFFER */
243 rect
.Top
= rect
.Left
= 0;
244 rect
.Bottom
= orig_scr
.dwSize
.Y
- 1;
245 rect
.Right
= orig_scr
.dwSize
.X
- 1;
248 origin
.X
= origin
.Y
= 0;
250 if (!WriteConsoleOutput(pdc_con_out
, ci_save
, orig_scr
.dwSize
,
255 if (SP
->visibility
!= 1)
258 /* Position cursor to the bottom left of the screen. */
260 PDC_gotoyx(PDC_get_buffer_rows() - 2, 0);
263 void PDC_scr_free(void)
270 pdc_atrtab
= (unsigned char *)NULL
;
273 /* open the physical screen -- allocate SP, miscellaneous intialization,
274 and may save the existing screen for later restoration */
276 int PDC_scr_open(int argc
, char **argv
)
278 COORD bufsize
, origin
;
281 CONSOLE_SCREEN_BUFFER_INFO csbi
;
284 PDC_LOG(("PDC_scr_open() - called\n"));
286 SP
= calloc(1, sizeof(SCREEN
));
287 pdc_atrtab
= calloc(PDC_COLOR_PAIRS
* PDC_OFFSET
, 1);
289 if (!SP
|| !pdc_atrtab
)
292 for (i
= 0; i
< 16; i
++)
293 curstoreal
[realtocurs
[i
]] = i
;
295 pdc_con_out
= GetStdHandle(STD_OUTPUT_HANDLE
);
296 pdc_con_in
= GetStdHandle(STD_INPUT_HANDLE
);
298 if (GetFileType(pdc_con_in
) != FILE_TYPE_CHAR
)
300 fprintf(stderr
, "\nRedirection is not supported.\n");
304 is_nt
= !(GetVersion() & 0x80000000);
306 GetConsoleScreenBufferInfo(pdc_con_out
, &csbi
);
307 GetConsoleScreenBufferInfo(pdc_con_out
, &orig_scr
);
308 GetConsoleMode(pdc_con_in
, &old_console_mode
);
310 /* preserve QuickEdit Mode setting for use in PDC_mouse_set() when
311 the mouse is not enabled -- other console input settings are
314 pdc_quick_edit
= old_console_mode
& 0x0040;
316 SP
->lines
= (str
= getenv("LINES")) ? atoi(str
) : PDC_get_rows();
317 SP
->cols
= (str
= getenv("COLS")) ? atoi(str
) : PDC_get_columns();
319 SP
->mouse_wait
= PDC_CLICK_PERIOD
;
322 if (SP
->lines
< 2 || SP
->lines
> csbi
.dwMaximumWindowSize
.Y
)
324 fprintf(stderr
, "LINES value must be >= 2 and <= %d: got %d\n",
325 csbi
.dwMaximumWindowSize
.Y
, SP
->lines
);
330 if (SP
->cols
< 2 || SP
->cols
> csbi
.dwMaximumWindowSize
.X
)
332 fprintf(stderr
, "COLS value must be >= 2 and <= %d: got %d\n",
333 csbi
.dwMaximumWindowSize
.X
, SP
->cols
);
338 SP
->orig_fore
= csbi
.wAttributes
& 0x0f;
339 SP
->orig_back
= (csbi
.wAttributes
& 0xf0) >> 4;
341 SP
->orig_attr
= TRUE
;
343 SP
->_restore
= PDC_RESTORE_NONE
;
345 if (getenv("PDC_RESTORE_SCREEN"))
347 /* Attempt to save the complete console buffer */
349 ci_save
= malloc(orig_scr
.dwSize
.X
* orig_scr
.dwSize
.Y
*
354 PDC_LOG(("PDC_scr_open() - malloc failure (1)\n"));
359 bufsize
.X
= orig_scr
.dwSize
.X
;
360 bufsize
.Y
= orig_scr
.dwSize
.Y
;
362 origin
.X
= origin
.Y
= 0;
364 rect
.Top
= rect
.Left
= 0;
365 rect
.Bottom
= orig_scr
.dwSize
.Y
- 1;
366 rect
.Right
= orig_scr
.dwSize
.X
- 1;
368 if (!ReadConsoleOutput(pdc_con_out
, ci_save
, bufsize
, origin
, &rect
))
370 /* We can't save the complete buffer, so try and save just
371 the displayed window */
376 bufsize
.X
= orig_scr
.srWindow
.Right
- orig_scr
.srWindow
.Left
+ 1;
377 bufsize
.Y
= orig_scr
.srWindow
.Bottom
- orig_scr
.srWindow
.Top
+ 1;
379 ci_save
= malloc(bufsize
.X
* bufsize
.Y
* sizeof(CHAR_INFO
));
383 PDC_LOG(("PDC_scr_open() - malloc failure (2)\n"));
388 origin
.X
= origin
.Y
= 0;
390 rect
.Top
= orig_scr
.srWindow
.Top
;
391 rect
.Left
= orig_scr
.srWindow
.Left
;
392 rect
.Bottom
= orig_scr
.srWindow
.Bottom
;
393 rect
.Right
= orig_scr
.srWindow
.Right
;
395 if (!ReadConsoleOutput(pdc_con_out
, ci_save
, bufsize
,
401 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
, NULL
,
402 GetLastError(), MAKELANGID(LANG_NEUTRAL
,
403 SUBLANG_DEFAULT
), LastError
, 256, NULL
);
405 PDC_LOG(("PDC_scr_open() - %s\n", LastError
));
413 SP
->_restore
= PDC_RESTORE_WINDOW
;
416 SP
->_restore
= PDC_RESTORE_BUFFER
;
419 SP
->_preserve
= (getenv("PDC_PRESERVE_SCREEN") != NULL
);
421 PDC_reset_prog_mode();
428 /* Calls SetConsoleWindowInfo with the given parameters, but fits them
429 if a scoll bar shrinks the maximum possible value. The rectangle
430 must at least fit in a half-sized window. */
432 static BOOL
_fit_console_window(HANDLE con_out
, CONST SMALL_RECT
*rect
)
437 if (SetConsoleWindowInfo(con_out
, TRUE
, rect
))
447 if (!SetConsoleWindowInfo(con_out
, TRUE
, &run
))
450 for (run
.Right
= rect
->Right
; run
.Right
>= mx
; run
.Right
--)
451 if (SetConsoleWindowInfo(con_out
, TRUE
, &run
))
457 for (run
.Bottom
= rect
->Bottom
; run
.Bottom
>= my
; run
.Bottom
--)
458 if (SetConsoleWindowInfo(con_out
, TRUE
, &run
))
464 /* the core of resize_term() */
466 int PDC_resize_screen(int nlines
, int ncols
)
471 if (nlines
< 2 || ncols
< 2)
474 max
= GetLargestConsoleWindowSize(pdc_con_out
);
476 rect
.Left
= rect
.Top
= 0;
477 rect
.Right
= ncols
- 1;
479 if (rect
.Right
> max
.X
)
482 rect
.Bottom
= nlines
- 1;
484 if (rect
.Bottom
> max
.Y
)
487 size
.X
= rect
.Right
+ 1;
488 size
.Y
= rect
.Bottom
+ 1;
490 _fit_console_window(pdc_con_out
, &rect
);
491 SetConsoleScreenBufferSize(pdc_con_out
, size
);
492 _fit_console_window(pdc_con_out
, &rect
);
493 SetConsoleScreenBufferSize(pdc_con_out
, size
);
494 SetConsoleActiveScreenBuffer(pdc_con_out
);
499 void PDC_reset_prog_mode(void)
501 PDC_LOG(("PDC_reset_prog_mode() - called.\n"));
508 bufsize
.X
= orig_scr
.srWindow
.Right
- orig_scr
.srWindow
.Left
+ 1;
509 bufsize
.Y
= orig_scr
.srWindow
.Bottom
- orig_scr
.srWindow
.Top
+ 1;
511 rect
.Top
= rect
.Left
= 0;
512 rect
.Bottom
= bufsize
.Y
- 1;
513 rect
.Right
= bufsize
.X
- 1;
515 SetConsoleScreenBufferSize(pdc_con_out
, bufsize
);
516 SetConsoleWindowInfo(pdc_con_out
, TRUE
, &rect
);
517 SetConsoleScreenBufferSize(pdc_con_out
, bufsize
);
518 SetConsoleActiveScreenBuffer(pdc_con_out
);
524 void PDC_reset_shell_mode(void)
526 PDC_LOG(("PDC_reset_shell_mode() - called.\n"));
530 SetConsoleScreenBufferSize(pdc_con_out
, orig_scr
.dwSize
);
531 SetConsoleWindowInfo(pdc_con_out
, TRUE
, &orig_scr
.srWindow
);
532 SetConsoleScreenBufferSize(pdc_con_out
, orig_scr
.dwSize
);
533 SetConsoleWindowInfo(pdc_con_out
, TRUE
, &orig_scr
.srWindow
);
534 SetConsoleActiveScreenBuffer(pdc_con_out
);
537 SetConsoleMode(pdc_con_in
, old_console_mode
);
540 void PDC_restore_screen_mode(int i
)
544 void PDC_save_screen_mode(int i
)
548 void PDC_init_pair(short pair
, short fg
, short bg
)
550 unsigned char att
, temp_bg
;
556 for (i
= 0; i
< PDC_OFFSET
; i
++)
558 att
= fg
| (bg
<< 4);
560 if (i
& (A_REVERSE
>> PDC_ATTR_SHIFT
))
561 att
= bg
| (fg
<< 4);
562 if (i
& (A_UNDERLINE
>> PDC_ATTR_SHIFT
))
564 if (i
& (A_INVIS
>> PDC_ATTR_SHIFT
))
567 att
= temp_bg
<< 4 | temp_bg
;
569 if (i
& (A_BOLD
>> PDC_ATTR_SHIFT
))
571 if (i
& (A_BLINK
>> PDC_ATTR_SHIFT
))
574 pdc_atrtab
[pair
* PDC_OFFSET
+ i
] = att
;
578 int PDC_pair_content(short pair
, short *fg
, short *bg
)
580 *fg
= realtocurs
[pdc_atrtab
[pair
* PDC_OFFSET
] & 0x0F];
581 *bg
= realtocurs
[(pdc_atrtab
[pair
* PDC_OFFSET
] & 0xF0) >> 4];
586 bool PDC_can_change_color(void)
591 int PDC_color_content(short color
, short *red
, short *green
, short *blue
)
595 if (!console_info
.Hwnd
)
596 _init_console_info();
598 col
= console_info
.ColorTable
[curstoreal
[color
]];
600 *red
= DIVROUND(GetRValue(col
) * 1000, 255);
601 *green
= DIVROUND(GetGValue(col
) * 1000, 255);
602 *blue
= DIVROUND(GetBValue(col
) * 1000, 255);
607 int PDC_init_color(short color
, short red
, short green
, short blue
)
609 if (!console_info
.Hwnd
)
610 _init_console_info();
612 console_info
.ColorTable
[curstoreal
[color
]] =
613 RGB(DIVROUND(red
* 255, 1000),
614 DIVROUND(green
* 255, 1000),
615 DIVROUND(blue
* 255, 1000));