Commit 6cdfb6e6 authored by Richard M. Stallman's avatar Richard M. Stallman
Browse files

Initial revision

parent dd3240b7
/* Terminal hooks for Windows NT port of GNU Emacs.
Copyright (C) 1992 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any later
version.
GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with GNU Emacs; see the file COPYING. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Tim Fleehart (apollo@online.com) 1-17-92
Geoff Voelker (voelker@cs.washington.edu) 9-12-93
*/
#include <stdlib.h>
#include <stdio.h>
#include "config.h"
#include <windows.h>
#include "lisp.h"
#include "frame.h"
#include "disptab.h"
#include "termhooks.h"
#include "ntinevt.h"
/* frrom window.c */
extern Lisp_Object Frecenter ();
/* from keyboard.c */
extern int detect_input_pending ();
/* from sysdep.c */
extern int read_input_pending ();
extern FRAME_PTR 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);
static void reassert_line_highlight (int, int);
static void insert_glyphs (GLYPH *start, int len);
static void write_glyphs (GLYPH *string, int len);
static void delete_glyphs (int n);
static void 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 (FRAME_PTR f);
static void update_end (FRAME_PTR f);
static void reset_kbd (void);
static void unset_kbd (void);
static int hl_mode (int new_highlight);
void
DebPrint ()
{
}
/* Init hook called in init_keyboard. */
void (*keyboard_init_hook)(void) = reset_kbd;
COORD cursor_coords;
HANDLE prev_screen, cur_screen;
UCHAR char_attr, char_attr_normal, char_attr_reverse;
HANDLE keyboard_handle;
/* Setting this as the ctrl handler prevents emacs from being killed when
* someone hits ^C in a 'suspended' session (child shell). */
BOOL
ctrl_c_handler (unsigned long type)
{
return (type == CTRL_C_EVENT) ? TRUE : FALSE;
}
/* If we're updating a frame, use it as the current frame
Otherwise, use the selected frame. */
#define PICK_FRAME() (updating_frame ? updating_frame : selected_frame)
/* Move the cursor to (row, col). */
void
move_cursor (int row, int col)
{
cursor_coords.X = col;
cursor_coords.Y = row;
if (updating_frame == NULL)
{
SetConsoleCursorPosition (cur_screen, cursor_coords);
}
}
/* Clear from cursor to end of screen. */
void
clear_to_end (void)
{
FRAME_PTR f = PICK_FRAME ();
clear_end_of_line (FRAME_WIDTH (f) - 1);
ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
}
/* Clear the frame. */
void
clear_frame (void)
{
SMALL_RECT scroll;
COORD dest;
CHAR_INFO fill;
FRAME_PTR f = PICK_FRAME ();
hl_mode (0);
scroll.Top = 0;
scroll.Bottom = FRAME_HEIGHT (f) - 1;
scroll.Left = 0;
scroll.Right = FRAME_WIDTH (f) - 1;
dest.Y = FRAME_HEIGHT (f);
dest.X = 0;
fill.Char.AsciiChar = 0x20;
fill.Attributes = char_attr;
ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
move_cursor (0, 0);
}
static GLYPH glyph_base[256];
static BOOL ceol_initialized = FALSE;
/* Clear from Cursor to end (what's "standout marker"?). */
void
clear_end_of_line (int end)
{
if (!ceol_initialized)
{
int i;
for (i = 0; i < 256; i++)
{
glyph_base[i] = SPACEGLYPH; /* empty space */
}
ceol_initialized = TRUE;
}
write_glyphs (glyph_base, end - cursor_coords.X); /* fencepost ? */
}
/* Insert n lines at vpos. if n is negative delete -n lines. */
void
ins_del_lines (int vpos, int n)
{
int i, nb, save_highlight;
SMALL_RECT scroll;
COORD dest;
CHAR_INFO fill;
FRAME_PTR 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;
ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
/* Here we have to deal with a win32 console flake: If the scroll
region looks like abc and we scroll c to a and fill with d we get
cbd... if we scroll block c one line at a time to a, we get cdd...
Emacs expects cdd consistently... So we have to deal with that
here... (this also occurs scrolling the same way in the other
direction. */
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);
}
/* Changes attribute to use when drawing characters to control. */
static int
hl_mode (int new_highlight)
{
static int highlight = 0;
int old_highlight;
old_highlight = highlight;
highlight = (new_highlight != 0);
if (highlight)
{
char_attr = char_attr_reverse;
}
else
{
char_attr = char_attr_normal;
}
return old_highlight;
}
/* Call this when about to modify line at position VPOS and change whether it
is highlighted. */
void
change_line_highlight (int new_highlight, int vpos, int first_unused_hpos)
{
hl_mode (new_highlight);
move_cursor (vpos, 0);
clear_end_of_line (first_unused_hpos);
}
/* External interface to control of standout mode. Call this when about to
* modify line at position VPOS and not change whether it is highlighted. */
void
reassert_line_highlight (int highlight, int vpos)
{
hl_mode (highlight);
vpos; /* pedantic compiler silencer */
}
#undef LEFT
#undef RIGHT
#define LEFT 1
#define RIGHT 0
void
scroll_line (int dist, int direction)
{
/* The idea here is to implement a horizontal scroll in one line to
implement delete and half of insert. */
SMALL_RECT scroll;
COORD dest;
CHAR_INFO fill;
FRAME_PTR 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;
ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
}
/* If start is zero insert blanks instead of a string at start ?. */
void
insert_glyphs (register GLYPH *start, register int len)
{
scroll_line (len, RIGHT);
/* Move len chars to the right starting at cursor_coords, fill with blanks */
if (start)
{
/* Print the first len characters of start, cursor_coords.X adjusted
by write_glyphs. */
write_glyphs (start, len);
}
else
{
clear_end_of_line (cursor_coords.X + len);
}
}
void
write_glyphs (register GLYPH *string, register int len)
{
register unsigned int glyph_len = GLYPH_TABLE_LENGTH;
Lisp_Object *glyph_table = GLYPH_TABLE_BASE;
FRAME_PTR f = PICK_FRAME ();
register char *ptr;
GLYPH glyph;
WORD *attrs;
char *chars;
int i;
attrs = alloca (len * sizeof (*attrs));
chars = alloca (len * sizeof (*chars));
if (attrs == NULL || chars == NULL)
{
printf ("alloca failed in write_glyphs\n");
return;
}
/* We have to deal with the glyph indirection...go over the glyph
buffer and extract the characters. */
ptr = chars;
while (--len >= 0)
{
glyph = *string++;
if (glyph > glyph_len)
{
*ptr++ = glyph & 0xFF;
continue;
}
GLYPH_FOLLOW_ALIASES (glyph_table, glyph_len, glyph);
if (GLYPH_FACE (fixfix, glyph) != 0)
printf ("Glyph face is %d\n", GLYPH_FACE (fixfix, glyph));
if (GLYPH_SIMPLE_P (glyph_table, glyph_len, glyph))
{
*ptr++ = glyph & 0xFF;
continue;
}
for (i = 0; i < GLYPH_LENGTH (glyph_table, glyph); i++)
{
*ptr++ = (GLYPH_STRING (glyph_table, glyph))[i];
}
}
/* Number of characters we have in the buffer. */
len = ptr-chars;
/* Fill in the attributes for these characters. */
memset (attrs, char_attr, len*sizeof (*attrs));
/* Write the attributes. */
if (!WriteConsoleOutputAttribute (cur_screen, attrs, len, cursor_coords, &i))
{
printf ("Failed writing console attributes.\n");
fflush (stdout);
}
/* Write the characters. */
if (!WriteConsoleOutputCharacter (cur_screen, chars, len, cursor_coords, &i))
{
printf ("Failed writing console characters.\n");
fflush (stdout);
}
cursor_coords.X += len;
move_cursor (cursor_coords.Y, cursor_coords.X);
}
void
delete_glyphs (int n)
{
/* delete chars means scroll chars from cursor_coords.X + n to
cursor_coords.X, anything beyond the edge of the screen should
come out empty... */
scroll_line (n, LEFT);
}
void
ring_bell (void)
{
Beep (666, 100);
}
/* Reset to the original console mode but don't get rid of our console
For suspending emacs. */
void
restore_console (void)
{
unset_kbd ();
SetConsoleActiveScreenBuffer (prev_screen);
}
/* Put our console back up, for ending a suspended session. */
void
take_console (void)
{
reset_kbd ();
SetConsoleActiveScreenBuffer (cur_screen);
}
void
reset_terminal_modes (void)
{
unset_kbd ();
SetConsoleActiveScreenBuffer (prev_screen);
CloseHandle (cur_screen);
cur_screen = NULL;
}
void
set_terminal_modes (void)
{
CONSOLE_CURSOR_INFO cci;
if (cur_screen == NULL)
{
reset_kbd ();
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);
}
SetConsoleActiveScreenBuffer (cur_screen);
/* make cursor big and visible */
cci.dwSize = 100;
cci.bVisible = TRUE;
(void) SetConsoleCursorInfo (cur_screen, &cci);
}
}
/* hmmm... perhaps these let us bracket screen changes so that we can flush
clumps rather than one-character-at-a-time...
we'll start with not moving the cursor while an update is in progress. */
void
update_begin (FRAME_PTR f)
{
}
void
update_end (FRAME_PTR f)
{
SetConsoleCursorPosition (cur_screen, cursor_coords);
}
void
set_terminal_window (int size)
{
}
void
unset_kbd (void)
{
SetConsoleMode (keyboard_handle, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT);
}
void
reset_kbd (void)
{
keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
}
typedef int (*term_hook) ();
void
initialize_win_nt_display (void)
{
CONSOLE_SCREEN_BUFFER_INFO info;
cursor_to_hook = (term_hook) move_cursor;
raw_cursor_to_hook = (term_hook) move_cursor;
clear_to_end_hook = (term_hook) clear_to_end;
clear_frame_hook = (term_hook) clear_frame;
clear_end_of_line_hook = (term_hook) clear_end_of_line;
ins_del_lines_hook = (term_hook) ins_del_lines;
change_line_highlight_hook = (term_hook) change_line_highlight;
reassert_line_highlight_hook = (term_hook) reassert_line_highlight;
insert_glyphs_hook = (term_hook) insert_glyphs;
write_glyphs_hook = (term_hook) write_glyphs;
delete_glyphs_hook = (term_hook) delete_glyphs;
ring_bell_hook = (term_hook) ring_bell;
reset_terminal_modes_hook = (term_hook) reset_terminal_modes;
set_terminal_modes_hook = (term_hook) set_terminal_modes;
set_terminal_window_hook = (term_hook) set_terminal_window;
update_begin_hook = (term_hook) update_begin;
update_end_hook = (term_hook) update_end;
read_socket_hook = win32_read_socket;
mouse_position_hook = win32_mouse_position;
prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
set_terminal_modes ();
GetConsoleScreenBufferInfo (cur_screen, &info);
meta_key = 1;
char_attr = info.wAttributes & 0xFF;
char_attr_normal = char_attr;
char_attr_reverse = ((char_attr & 0xf) << 4) + ((char_attr & 0xf0) >> 4);
FRAME_HEIGHT (selected_frame) = info.dwSize.Y; /* lines per page */
FRAME_WIDTH (selected_frame) = info.dwSize.X; /* characters per line */
move_cursor (0, 0);
clear_frame ();
}
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);
char_attr_reverse = XFASTINT (background) + (XFASTINT (foreground) << 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;
}
void
pixel_to_glyph_coords (FRAME_PTR 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 (FRAME_PTR f, int x, int y, int *pix_x, int *pix_y)
{
*pix_x = x;
*pix_y = y;
}
_VOID_
syms_of_ntterm ()
{
defsubr (&Sset_screen_color);
defsubr (&Sset_cursor_size);
}
/* Input event support for Windows NT port of GNU Emacs.
Copyright (C) 1992, 1993 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any later
version.
GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with GNU Emacs; see the file COPYING. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Drew Bliss 01-Oct-93
Adapted from ntkbd.c by Tim Fleehart
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include "lisp.h"
#include "frame.h"
#include "blockinput.h"