/* 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 #include #include "config.h" #include #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); }