#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include "lisp.h"
#include "charset.h"
#include "coding.h"
#include "disptab.h"
#include "termhooks.h"
#include "dispextern.h"
#undef HAVE_WINDOW_SYSTEM
#include "frame.h"
#include "w32inevt.h"
#define min(a, b) ((a) < (b) ? (a) : (b))
extern Lisp_Object Frecenter ();
extern int detect_input_pending ();
extern int read_input_pending ();
extern struct frame * updating_frame;
extern int meta_key;
static void move_cursor (int row, int col);
static void clear_to_end (void);
static void clear_frame (void);
static void clear_end_of_line (int);
static void ins_del_lines (int vpos, int n);
static void change_line_highlight (int, int, int, int);
static void reassert_line_highlight (int, int);
static void insert_glyphs (struct glyph *start, int len);
static void write_glyphs (struct glyph *string, int len);
static void delete_glyphs (int n);
void w32_sys_ring_bell (void);
static void reset_terminal_modes (void);
static void set_terminal_modes (void);
static void set_terminal_window (int size);
static void update_begin (struct frame * f);
static void update_end (struct frame * f);
static WORD w32_face_attributes (struct frame *f, int face_id);
static int hl_mode (int new_highlight);
static COORD cursor_coords;
static HANDLE prev_screen, cur_screen;
static WORD char_attr_normal;
static DWORD prev_console_mode;
#ifndef USE_SEPARATE_SCREEN
static CONSOLE_CURSOR_INFO prev_console_cursor;
#endif
int w32_use_full_screen_buffer;
HANDLE keyboard_handle;
BOOL
ctrl_c_handler (unsigned long type)
{
return (!noninteractive
&& (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT));
}
#define PICK_FRAME() (updating_frame ? updating_frame : SELECTED_FRAME ())
void
move_cursor (int row, int col)
{
cursor_coords.X = col;
cursor_coords.Y = row;
if (updating_frame == (struct frame *) NULL)
{
SetConsoleCursorPosition (cur_screen, cursor_coords);
}
}
void
clear_to_end (void)
{
struct frame * f = PICK_FRAME ();
clear_end_of_line (FRAME_WIDTH (f) - 1);
ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
}
void
clear_frame (void)
{
struct frame * f = PICK_FRAME ();
COORD dest;
int n;
DWORD r;
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
hl_mode (0);
n = FRAME_HEIGHT (f) * info.dwSize.X;
dest.X = dest.Y = 0;
FillConsoleOutputAttribute (cur_screen, char_attr_normal, n, dest, &r);
FillConsoleOutputCharacter (cur_screen, ' ', n, dest, &r);
move_cursor (0, 0);
}
static struct glyph glyph_base[256];
static BOOL ceol_initialized = FALSE;
void
clear_end_of_line (int end)
{
if (!ceol_initialized)
{
int i;
for (i = 0; i < 256; i++)
{
memcpy (&glyph_base[i], &space_glyph, sizeof (struct glyph));
}
ceol_initialized = TRUE;
}
write_glyphs (glyph_base, end - cursor_coords.X);
}
void
ins_del_lines (int vpos, int n)
{
int i, nb, save_highlight;
SMALL_RECT scroll;
COORD dest;
CHAR_INFO fill;
struct frame * f = PICK_FRAME ();
if (n < 0)
{
scroll.Top = vpos - n;
scroll.Bottom = FRAME_HEIGHT (f);
dest.Y = vpos;
}
else
{
scroll.Top = vpos;
scroll.Bottom = FRAME_HEIGHT (f) - n;
dest.Y = vpos + n;
}
scroll.Left = 0;
scroll.Right = FRAME_WIDTH (f);
dest.X = 0;
save_highlight = hl_mode (0);
fill.Char.AsciiChar = 0x20;
fill.Attributes = char_attr_normal;
ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
if (n > 0)
{
if (scroll.Bottom < dest.Y)
{
for (i = scroll.Bottom; i < dest.Y; i++)
{
move_cursor (i, 0);
clear_end_of_line (FRAME_WIDTH (f));
}
}
}
else
{
nb = dest.Y + (scroll.Bottom - scroll.Top) + 1;
if (nb < scroll.Top)
{
for (i = nb; i < scroll.Top; i++)
{
move_cursor (i, 0);
clear_end_of_line (FRAME_WIDTH (f));
}
}
}
cursor_coords.X = 0;
cursor_coords.Y = vpos;
hl_mode (save_highlight);
}
static int
hl_mode (int new_highlight)
{
static int highlight = 0;
int old_highlight;
old_highlight = highlight;
highlight = (new_highlight != 0);
return old_highlight;
}
void
change_line_highlight (int new_highlight, int vpos, int y,
int first_unused_hpos)
{
hl_mode (new_highlight);
move_cursor (vpos, 0);
clear_end_of_line (first_unused_hpos);
}
void
reassert_line_highlight (int highlight, int vpos)
{
hl_mode (highlight);
vpos;
}
#undef LEFT
#undef RIGHT
#define LEFT 1
#define RIGHT 0
void
scroll_line (int dist, int direction)
{
SMALL_RECT scroll;
COORD dest;
CHAR_INFO fill;
struct frame * f = PICK_FRAME ();
scroll.Top = cursor_coords.Y;
scroll.Bottom = cursor_coords.Y;
if (direction == LEFT)
{
scroll.Left = cursor_coords.X + dist;
scroll.Right = FRAME_WIDTH (f) - 1;
}
else
{
scroll.Left = cursor_coords.X;
scroll.Right = FRAME_WIDTH (f) - dist - 1;
}
dest.X = cursor_coords.X;
dest.Y = cursor_coords.Y;
fill.Char.AsciiChar = 0x20;
fill.Attributes = char_attr_normal;
ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
}
void
insert_glyphs (register struct glyph *start, register int len)
{
scroll_line (len, RIGHT);
if (start)
{
write_glyphs (start, len);
}
else
{
clear_end_of_line (cursor_coords.X + len);
}
}
void
write_glyphs (register struct glyph *string, register int len)
{
int produced, consumed;
DWORD r;
struct frame * f = PICK_FRAME ();
WORD char_attr;
unsigned char conversion_buffer[1024];
int conversion_buffer_size = sizeof conversion_buffer;
if (len <= 0)
return;
terminal_coding.mode &= ~CODING_MODE_LAST_BLOCK;
while (len > 0)
{
int face_id = string->face_id;
int n;
for (n = 1; n < len; ++n)
if (string[n].face_id != face_id)
break;
char_attr = w32_face_attributes (f, face_id);
while (n > 0)
{
produced = encode_terminal_code (string, conversion_buffer,
n, conversion_buffer_size,
&consumed);
if (produced > 0)
{
if (!FillConsoleOutputAttribute (cur_screen, char_attr,
produced, cursor_coords, &r))
{
printf ("Failed writing console attributes: %d\n",
GetLastError ());
fflush (stdout);
}
if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer,
produced, cursor_coords, &r))
{
printf ("Failed writing console characters: %d\n",
GetLastError ());
fflush (stdout);
}
cursor_coords.X += produced;
move_cursor (cursor_coords.Y, cursor_coords.X);
}
len -= consumed;
n -= consumed;
string += consumed;
}
}
if (CODING_REQUIRE_FLUSHING (&terminal_coding))
{
terminal_coding.mode |= CODING_MODE_LAST_BLOCK;
encode_coding (&terminal_coding, "", conversion_buffer,
0, conversion_buffer_size);
if (terminal_coding.produced > 0)
{
if (!FillConsoleOutputAttribute (cur_screen, char_attr_normal,
terminal_coding.produced,
cursor_coords, &r))
{
printf ("Failed writing console attributes: %d\n",
GetLastError ());
fflush (stdout);
}
if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer,
produced, cursor_coords, &r))
{
printf ("Failed writing console characters: %d\n",
GetLastError ());
fflush (stdout);
}
}
}
}
void
delete_glyphs (int n)
{
scroll_line (n, LEFT);
}
static unsigned int sound_type = 0xFFFFFFFF;
#define MB_EMACS_SILENT (0xFFFFFFFF - 1)
void
w32_sys_ring_bell (void)
{
if (sound_type == 0xFFFFFFFF)
{
Beep (666, 100);
}
else if (sound_type == MB_EMACS_SILENT)
{
}
else
MessageBeep (sound_type);
}
DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0,
"Set the sound generated when the bell is rung.\n\
SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent\n\
to use the corresponding system sound for the bell. The 'silent sound\n\
prevents Emacs from making any sound at all.\n\
SOUND is nil to use the normal beep.")
(sound)
Lisp_Object sound;
{
CHECK_SYMBOL (sound, 0);
if (NILP (sound))
sound_type = 0xFFFFFFFF;
else if (EQ (sound, intern ("asterisk")))
sound_type = MB_ICONASTERISK;
else if (EQ (sound, intern ("exclamation")))
sound_type = MB_ICONEXCLAMATION;
else if (EQ (sound, intern ("hand")))
sound_type = MB_ICONHAND;
else if (EQ (sound, intern ("question")))
sound_type = MB_ICONQUESTION;
else if (EQ (sound, intern ("ok")))
sound_type = MB_OK;
else if (EQ (sound, intern ("silent")))
sound_type = MB_EMACS_SILENT;
else
sound_type = 0xFFFFFFFF;
return sound;
}
void
reset_terminal_modes (void)
{
hl_mode (0);
#ifdef USE_SEPARATE_SCREEN
SetConsoleActiveScreenBuffer (prev_screen);
#else
SetConsoleCursorInfo (prev_screen, &prev_console_cursor);
#endif
SetConsoleMode (keyboard_handle, prev_console_mode);
}
void
set_terminal_modes (void)
{
CONSOLE_CURSOR_INFO cci;
hl_mode (0);
cci.dwSize = 99;
cci.bVisible = TRUE;
(void) SetConsoleCursorInfo (cur_screen, &cci);
SetConsoleActiveScreenBuffer (cur_screen);
SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
Fset_input_mode (Qnil, Qnil, make_number (2), Qnil);
}
void
update_begin (struct frame * f)
{
hl_mode (0);
}
void
update_end (struct frame * f)
{
hl_mode (0);
SetConsoleCursorPosition (cur_screen, cursor_coords);
}
void
set_terminal_window (int size)
{
}
static WORD
w32_face_attributes (f, face_id)
struct frame *f;
int face_id;
{
WORD char_attr;
int highlight_on_p;
struct face *face = FACE_FROM_ID (f, face_id);
highlight_on_p = hl_mode (0);
hl_mode (highlight_on_p);
xassert (face != NULL);
char_attr = char_attr_normal;
if (face->foreground != FACE_TTY_DEFAULT_FG_COLOR
&& face->foreground != FACE_TTY_DEFAULT_COLOR)
char_attr = (char_attr & 0xfff0) + (face->foreground % 16);
if (face->background != FACE_TTY_DEFAULT_BG_COLOR
&& face->background != FACE_TTY_DEFAULT_COLOR)
char_attr = (char_attr & 0xff0f) + ((face->background % 16) << 4);
if (((char_attr & 0x00f0) >> 4) == (char_attr & 0x000f))
char_attr ^= 0x0007;
if (face->tty_reverse_p || highlight_on_p)
char_attr = (char_attr & 0xff00) + ((char_attr & 0x000f) << 4)
+ ((char_attr & 0x00f0) >> 4);
return char_attr;
}
extern char unspecified_fg[], unspecified_bg[];
Lisp_Object
vga_stdcolor_name (int idx)
{
static char *vga_colors[16] = {
"black", "blue", "green", "cyan", "red", "magenta", "brown",
"lightgray", "darkgray", "lightblue", "lightgreen", "lightcyan",
"lightred", "lightmagenta", "yellow", "white"
};
extern Lisp_Object Qunspecified;
if (idx >= 0 && idx < sizeof (vga_colors) / sizeof (vga_colors[0]))
return build_string (vga_colors[idx]);
else
return Qunspecified;
}
typedef int (*term_hook) ();
void
initialize_w32_display (void)
{
CONSOLE_SCREEN_BUFFER_INFO info;
cursor_to_hook = move_cursor;
raw_cursor_to_hook = move_cursor;
clear_to_end_hook = clear_to_end;
clear_frame_hook = clear_frame;
clear_end_of_line_hook = clear_end_of_line;
ins_del_lines_hook = ins_del_lines;
change_line_highlight_hook = change_line_highlight;
reassert_line_highlight_hook = reassert_line_highlight;
insert_glyphs_hook = insert_glyphs;
write_glyphs_hook = write_glyphs;
delete_glyphs_hook = delete_glyphs;
ring_bell_hook = w32_sys_ring_bell;
reset_terminal_modes_hook = reset_terminal_modes;
set_terminal_modes_hook = set_terminal_modes;
set_terminal_window_hook = set_terminal_window;
update_begin_hook = update_begin;
update_end_hook = update_end;
read_socket_hook = w32_console_read_socket;
mouse_position_hook = w32_console_mouse_position;
estimate_mode_line_height_hook = 0;
init_crit ();
keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
GetConsoleMode (keyboard_handle, &prev_console_mode);
prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
#ifdef USE_SEPARATE_SCREEN
cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
0, NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL);
if (cur_screen == INVALID_HANDLE_VALUE)
{
printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
printf ("LastError = 0x%lx\n", GetLastError ());
fflush (stdout);
exit (0);
}
#else
cur_screen = prev_screen;
GetConsoleCursorInfo (prev_screen, &prev_console_cursor);
#endif
{
char * lines = getenv("LINES");
char * columns = getenv("COLUMNS");
if (lines != NULL && columns != NULL)
{
SMALL_RECT new_win_dims;
COORD new_size;
new_size.X = atoi (columns);
new_size.Y = atoi (lines);
GetConsoleScreenBufferInfo (cur_screen, &info);
new_win_dims.Top = 0;
new_win_dims.Left = 0;
new_win_dims.Bottom = min (new_size.Y, info.dwSize.Y) - 1;
new_win_dims.Right = min (new_size.X, info.dwSize.X) - 1;
SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
SetConsoleScreenBufferSize (cur_screen, new_size);
new_win_dims.Top = 0;
new_win_dims.Left = 0;
new_win_dims.Bottom = new_size.Y - 1;
new_win_dims.Right = new_size.X - 1;
SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
}
}
GetConsoleScreenBufferInfo (cur_screen, &info);
meta_key = 1;
char_attr_normal = info.wAttributes;
hl_mode (0);
if (w32_use_full_screen_buffer)
{
FRAME_HEIGHT (SELECTED_FRAME ()) = info.dwSize.Y;
SET_FRAME_WIDTH (SELECTED_FRAME (), info.dwSize.X);
}
else
{
FRAME_HEIGHT (SELECTED_FRAME ()) = 1 + info.srWindow.Bottom -
info.srWindow.Top;
SET_FRAME_WIDTH (SELECTED_FRAME (), 1 + info.srWindow.Right -
info.srWindow.Left);
}
w32_initialize_display_info (build_string ("Console"));
}
DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
"Set screen colors.")
(foreground, background)
Lisp_Object foreground;
Lisp_Object background;
{
char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
Frecenter (Qnil);
return Qt;
}
DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
"Set cursor size.")
(size)
Lisp_Object size;
{
CONSOLE_CURSOR_INFO cci;
cci.dwSize = XFASTINT (size);
cci.bVisible = TRUE;
(void) SetConsoleCursorInfo (cur_screen, &cci);
return Qt;
}
#ifndef HAVE_NTGUI
void
pixel_to_glyph_coords (struct frame * f, int pix_x, int pix_y, int *x, int *y,
void *bounds, int noclip)
{
*x = pix_x;
*y = pix_y;
}
void
glyph_to_pixel_coords (struct window * f, int x, int y, int *pix_x, int *pix_y)
{
*pix_x = x;
*pix_y = y;
}
#endif
void
syms_of_ntterm ()
{
DEFVAR_BOOL ("w32-use-full-screen-buffer",
&w32_use_full_screen_buffer,
"Non-nil means make terminal frames use the full screen buffer dimensions.\n\
This is desirable when running Emacs over telnet, and is the default.\n\
A value of nil means use the current console window dimensions; this\n\
may be preferrable when working directly at the console with a large\n\
scroll-back buffer.");
w32_use_full_screen_buffer = 1;
defsubr (&Sset_screen_color);
defsubr (&Sset_cursor_size);
defsubr (&Sset_message_beep);
}