X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/a878555182908d9daa090e5c61ff95147c2b9df4..660872b63b75b61d11b09471b5a254e1e5db3c1c:/src/msdos.c diff --git a/src/msdos.c b/src/msdos.c index 2f19901a20..79ac0b9026 100644 --- a/src/msdos.c +++ b/src/msdos.c @@ -1,5 +1,6 @@ -/* MS-DOS specific C utilities. - Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. +/* MS-DOS specific C utilities. -*- coding: raw-text -*- + Copyright (C) 1993, 94, 95, 96, 97, 1999, 2000, 2001 + Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -29,24 +30,39 @@ Boston, MA 02111-1307, USA. */ #include "lisp.h" #include #include +#include #include #include #include #include +#include /* for bzero and string functions */ #include /* for _fixpath */ +#include /* for chdir, dup, dup2, etc. */ #if __DJGPP__ >= 2 #include +#include /* for setmode */ +#include /* for __dpmi_xxx stuff */ +#include /* for _farsetsel, _farnspokeb */ #include /* for _USE_LFN */ +#include /* for cputs */ #endif -#include "dosfns.h" #include "msdos.h" #include "systime.h" #include "termhooks.h" +#include "termchar.h" #include "dispextern.h" +#include "dosfns.h" #include "termopts.h" +#include "charset.h" +#include "coding.h" +#include "disptab.h" #include "frame.h" #include "window.h" +#include "buffer.h" +#include "commands.h" +#include "blockinput.h" +#include "keyboard.h" #include #include #include @@ -58,9 +74,14 @@ Boston, MA 02111-1307, USA. */ #define _USE_LFN 0 #endif +#ifndef _dos_ds +#define _dos_ds _go32_info_block.selector_for_linear_memory +#endif + #if __DJGPP__ > 1 #include +#include "syssignal.h" #ifndef SYSTEM_MALLOC @@ -105,6 +126,10 @@ event_timestamp () * Mouse buttons are numbered from left to right and also zero based. */ +/* This used to be in termhooks.h, but mainstream Emacs code no longer + uses it, and it was removed... */ +#define NUM_MOUSE_BUTTONS (5) + int have_mouse; /* 0: no, 1: enabled, -1: disabled */ static int mouse_visible; @@ -144,6 +169,56 @@ mouse_off () } } +static void +mouse_setup_buttons (int n_buttons) +{ + if (n_buttons == 3) + { + mouse_button_count = 3; + mouse_button_translate[0] = 0; /* Left */ + mouse_button_translate[1] = 2; /* Middle */ + mouse_button_translate[2] = 1; /* Right */ + } + else /* two, what else? */ + { + mouse_button_count = 2; + mouse_button_translate[0] = 0; + mouse_button_translate[1] = 1; + } +} + +DEFUN ("msdos-set-mouse-buttons", Fmsdos_set_mouse_buttons, Smsdos_set_mouse_buttons, + 1, 1, "NSet number of mouse buttons to: ", + "Set the number of mouse buttons to use by Emacs.\n\ +This is useful with mice that report the number of buttons inconsistently,\n\ +e.g., if the number of buttons is reported as 3, but Emacs only sees 2 of\n\ +them. This happens with wheeled mice on Windows 9X, for example.") + (nbuttons) + Lisp_Object nbuttons; +{ + int n; + + CHECK_NUMBER (nbuttons, 0); + n = XINT (nbuttons); + if (n < 2 || n > 3) + Fsignal (Qargs_out_of_range, + Fcons (build_string ("only 2 or 3 mouse buttons are supported"), + Fcons (nbuttons, Qnil))); + mouse_setup_buttons (n); + return Qnil; +} + +static void +mouse_get_xy (int *x, int *y) +{ + union REGS regs; + + regs.x.ax = 0x0003; + int86 (0x33, ®s, ®s); + *x = regs.x.cx / 8; + *y = regs.x.dx / 8; +} + void mouse_moveto (x, y) int x, y; @@ -190,15 +265,23 @@ mouse_released (b, xp, yp) return (regs.x.bx != 0); } -static void -mouse_get_xy (int *x, int *y) +static int +mouse_button_depressed (b, xp, yp) + int b, *xp, *yp; { union REGS regs; + if (b >= mouse_button_count) + return 0; regs.x.ax = 0x0003; int86 (0x33, ®s, ®s); - *x = regs.x.cx / 8; - *y = regs.x.dx / 8; + if ((regs.x.bx & (1 << mouse_button_translate[b])) != 0) + { + *xp = regs.x.cx / 8; + *yp = regs.x.dx / 8; + return 1; + } + return 0; } void @@ -210,17 +293,18 @@ mouse_get_pos (f, insist, bar_window, part, x, y, time) unsigned long *time; { int ix, iy; - union REGS regs; + Lisp_Object frame, tail; - regs.x.ax = 0x0003; - int86 (0x33, ®s, ®s); - *f = selected_frame; + /* Clear the mouse-moved flag for every frame on this display. */ + FOR_EACH_FRAME (tail, frame) + XFRAME (frame)->mouse_moved = 0; + + *f = SELECTED_FRAME(); *bar_window = Qnil; mouse_get_xy (&ix, &iy); - selected_frame->mouse_moved = 0; - *x = make_number (ix); - *y = make_number (iy); *time = event_timestamp (); + *x = make_number (mouse_last_x = ix); + *y = make_number (mouse_last_y = iy); } static void @@ -229,11 +313,27 @@ mouse_check_moved () int x, y; mouse_get_xy (&x, &y); - selected_frame->mouse_moved |= (x != mouse_last_x || y != mouse_last_y); + SELECTED_FRAME()->mouse_moved |= (x != mouse_last_x || y != mouse_last_y); mouse_last_x = x; mouse_last_y = y; } +/* Force the mouse driver to ``forget'' about any button clicks until + now. */ +static void +mouse_clear_clicks (void) +{ + int b; + + for (b = 0; b < mouse_button_count; b++) + { + int dummy_x, dummy_y; + + (void) mouse_pressed (b, &dummy_x, &dummy_y); + (void) mouse_released (b, &dummy_x, &dummy_y); + } +} + void mouse_init () { @@ -245,6 +345,12 @@ mouse_init () regs.x.ax = 0x0021; int86 (0x33, ®s, ®s); + /* Reset the mouse last press/release info. It seems that Windows + doesn't do that automatically when function 21h is called, which + causes Emacs to ``remember'' the click that switched focus to the + window just before Emacs was started from that window. */ + mouse_clear_clicks (); + regs.x.ax = 0x0007; regs.x.cx = 0; regs.x.dx = 8 * (ScreenCols () - 1); @@ -286,32 +392,71 @@ static int startup_pos_X; static int startup_pos_Y; static unsigned char startup_screen_attrib; +static clock_t startup_time; + static int term_setup_done; +static unsigned short outside_cursor; + /* Similar to the_only_frame. */ struct x_output the_only_x_display; -/* This is never dereferenced. */ -Display *x_current_display; +/* Support for DOS/V (allows Japanese characters to be displayed on + standard, non-Japanese, ATs). Only supported for DJGPP v2 and later. */ + +/* Holds the address of the text-mode screen buffer. */ +static unsigned long screen_old_address = 0; +/* Segment and offset of the virtual screen. If 0, DOS/V is NOT loaded. */ +static unsigned short screen_virtual_segment = 0; +static unsigned short screen_virtual_offset = 0; +/* A flag to control how to display unibyte 8-bit characters. */ +extern int unibyte_display_via_language_environment; + +Lisp_Object Qbar; + +#if __DJGPP__ > 1 +/* Update the screen from a part of relocated DOS/V screen buffer which + begins at OFFSET and includes COUNT characters. */ +static void +dosv_refresh_virtual_screen (int offset, int count) +{ + __dpmi_regs regs; + if (offset < 0 || count < 0) /* paranoia; invalid values crash DOS/V */ + return; -#define SCREEN_SET_CURSOR() \ - if (current_pos_X != new_pos_X || current_pos_Y != new_pos_Y) \ - ScreenSetCursor (current_pos_Y = new_pos_Y, current_pos_X = new_pos_X) + regs.h.ah = 0xff; /* update relocated screen */ + regs.x.es = screen_virtual_segment; + regs.x.di = screen_virtual_offset + offset; + regs.x.cx = count; + __dpmi_int (0x10, ®s); +} +#endif -static +static void dos_direct_output (y, x, buf, len) int y; int x; char *buf; int len; { - int t = (int) ScreenPrimary + 2 * (x + y * screen_size_X); - + int t0 = 2 * (x + y * screen_size_X); + int t = t0 + (int) ScreenPrimary; + int l0 = len; + +#if (__DJGPP__ < 2) while (--len >= 0) { dosmemput (buf++, 1, t); t += 2; } +#else + /* This is faster. */ + for (_farsetsel (_dos_ds); --len >= 0; t += 2, buf++) + _farnspokeb (t, *buf); + + if (screen_virtual_segment) + dosv_refresh_virtual_screen (t0, l0); +#endif } #endif @@ -323,29 +468,29 @@ do_visible_bell (xorattr) unsigned char xorattr; { asm volatile - (" movb $1,%%dl -visible_bell_0: - movl _ScreenPrimary,%%eax - call dosmemsetup - movl %%eax,%%ebx - movl %1,%%ecx - movb %0,%%al - incl %%ebx -visible_bell_1: - xorb %%al,%%gs:(%%ebx) - addl $2,%%ebx - decl %%ecx - jne visible_bell_1 - decb %%dl - jne visible_bell_3 -visible_bell_2: - movzwl %%ax,%%eax - movzwl %%ax,%%eax - movzwl %%ax,%%eax - movzwl %%ax,%%eax - decw %%cx - jne visible_bell_2 - jmp visible_bell_0 + (" movb $1,%%dl \n\ +visible_bell_0: \n\ + movl _ScreenPrimary,%%eax \n\ + call dosmemsetup \n\ + movl %%eax,%%ebx \n\ + movl %1,%%ecx \n\ + movb %0,%%al \n\ + incl %%ebx \n\ +visible_bell_1: \n\ + xorb %%al,%%gs:(%%ebx) \n\ + addl $2,%%ebx \n\ + decl %%ecx \n\ + jne visible_bell_1 \n\ + decb %%dl \n\ + jne visible_bell_3 \n\ +visible_bell_2: \n\ + movzwl %%ax,%%eax \n\ + movzwl %%ax,%%eax \n\ + movzwl %%ax,%%eax \n\ + movzwl %%ax,%%eax \n\ + decw %%cx \n\ + jne visible_bell_2 \n\ + jmp visible_bell_0 \n\ visible_bell_3:" : /* no output */ : "m" (xorattr), "g" (screen_size) @@ -365,17 +510,52 @@ ScreenVisualBell (void) #ifndef HAVE_X_WINDOWS +static int blink_bit = -1; /* the state of the blink bit at startup */ + /* Enable bright background colors. */ static void bright_bg (void) { union REGS regs; + /* Remember the original state of the blink/bright-background bit. + It is stored at 0040:0065h in the BIOS data area. */ + if (blink_bit == -1) + blink_bit = (_farpeekb (_dos_ds, 0x465) & 0x20) == 0x20; + regs.h.bl = 0; regs.x.ax = 0x1003; int86 (0x10, ®s, ®s); } +/* Disable bright background colors (and enable blinking) if we found + the video system in that state at startup. */ +static void +maybe_enable_blinking (void) +{ + if (blink_bit == 1) + { + union REGS regs; + + regs.h.bl = 1; + regs.x.ax = 0x1003; + int86 (0x10, ®s, ®s); + } +} + +/* Return non-zero if the system has a VGA adapter. */ +static int +vga_installed (void) +{ + union REGS regs; + + regs.x.ax = 0x1a00; + int86 (0x10, ®s, ®s); + if (regs.h.al == 0x1a && regs.h.bl > 5 && regs.h.bl < 13) + return 1; + return 0; +} + /* Set the screen dimensions so that it can show no less than ROWS x COLS frame. */ @@ -393,13 +573,8 @@ dos_set_window_size (rows, cols) if (*rows == current_rows && *cols == current_cols) return; - /* Do we have a VGA? */ - regs.x.ax = 0x1a00; - int86 (0x10, ®s, ®s); - if (regs.h.al == 0x1a && regs.h.bl > 5 && regs.h.bl < 13) - have_vga = 1; - mouse_off (); + have_vga = vga_installed (); /* If the user specified a special video mode for these dimensions, use that mode. */ @@ -503,8 +678,35 @@ dos_set_window_size (rows, cols) *rows = ScreenRows (); *cols = ScreenCols (); + /* Update Emacs' notion of screen dimensions. */ + screen_size_X = *cols; + screen_size_Y = *rows; + screen_size = *cols * *rows; + +#if __DJGPP__ > 1 + /* If the dimensions changed, the mouse highlight info is invalid. */ + if (current_rows != *rows || current_cols != *cols) + { + struct frame *f = SELECTED_FRAME(); + struct display_info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Lisp_Object window = dpyinfo->mouse_face_window; + + if (! NILP (window) && XFRAME (XWINDOW (window)->frame) == f) + { + dpyinfo->mouse_face_beg_row = dpyinfo->mouse_face_beg_col = -1; + dpyinfo->mouse_face_end_row = dpyinfo->mouse_face_end_col = -1; + dpyinfo->mouse_face_window = Qnil; + } + } +#endif + /* Enable bright background colors. */ bright_bg (); + + /* FIXME: I'm not sure the above will run at all on DOS/V. But let's + be defensive anyway. */ + if (screen_virtual_segment) + dosv_refresh_virtual_screen (0, *cols * *rows); } /* If we write a character in the position where the mouse is, @@ -525,8 +727,132 @@ mouse_off_maybe () mouse_off (); } -static -IT_ring_bell () +#define DEFAULT_CURSOR_START (-1) +#define DEFAULT_CURSOR_WIDTH (-1) +#define BOX_CURSOR_WIDTH (-32) + +/* Set cursor to begin at scan line START_LINE in the character cell + and extend for WIDTH scan lines. Scan lines are counted from top + of the character cell, starting from zero. */ +static void +msdos_set_cursor_shape (struct frame *f, int start_line, int width) +{ +#if __DJGPP__ > 1 + unsigned desired_cursor; + __dpmi_regs regs; + int max_line, top_line, bot_line; + + /* Avoid the costly BIOS call if F isn't the currently selected + frame. Allow for NULL as unconditionally meaning the selected + frame. */ + if (f && f != SELECTED_FRAME()) + return; + + /* The character cell size in scan lines is stored at 40:85 in the + BIOS data area. */ + max_line = _farpeekw (_dos_ds, 0x485) - 1; + switch (max_line) + { + default: /* this relies on CGA cursor emulation being ON! */ + case 7: + bot_line = 7; + break; + case 9: + bot_line = 9; + break; + case 13: + bot_line = 12; + break; + case 15: + bot_line = 14; + break; + } + + if (width < 0) + { + if (width == BOX_CURSOR_WIDTH) + { + top_line = 0; + bot_line = max_line; + } + else if (start_line != DEFAULT_CURSOR_START) + { + top_line = start_line; + bot_line = top_line - width - 1; + } + else if (width != DEFAULT_CURSOR_WIDTH) + { + top_line = 0; + bot_line = -1 - width; + } + else + top_line = bot_line + 1; + } + else if (width == 0) + { + /* [31, 0] seems to DTRT for all screen sizes. */ + top_line = 31; + bot_line = 0; + } + else /* WIDTH is positive */ + { + if (start_line != DEFAULT_CURSOR_START) + bot_line = start_line; + top_line = bot_line - (width - 1); + } + + /* If the current cursor shape is already what they want, we are + history here. */ + desired_cursor = ((top_line & 0x1f) << 8) | (bot_line & 0x1f); + if (desired_cursor == _farpeekw (_dos_ds, 0x460)) + return; + + regs.h.ah = 1; + regs.x.cx = desired_cursor; + __dpmi_int (0x10, ®s); +#endif /* __DJGPP__ > 1 */ +} + +static void +IT_set_cursor_type (struct frame *f, Lisp_Object cursor_type) +{ + if (EQ (cursor_type, Qbar)) + { + /* Just BAR means the normal EGA/VGA cursor. */ + msdos_set_cursor_shape (f, DEFAULT_CURSOR_START, DEFAULT_CURSOR_WIDTH); + } + else if (CONSP (cursor_type) && EQ (XCAR (cursor_type), Qbar)) + { + Lisp_Object bar_parms = XCDR (cursor_type); + int width; + + if (INTEGERP (bar_parms)) + { + /* Feature: negative WIDTH means cursor at the top + of the character cell, zero means invisible cursor. */ + width = XINT (bar_parms); + msdos_set_cursor_shape (f, width >= 0 ? DEFAULT_CURSOR_START : 0, + width); + } + else if (CONSP (bar_parms) + && INTEGERP (XCAR (bar_parms)) + && INTEGERP (XCDR (bar_parms))) + { + int start_line = XINT (XCDR (bar_parms)); + + width = XINT (XCAR (bar_parms)); + msdos_set_cursor_shape (f, start_line, width); + } + } + else + /* Treat anything unknown as "box cursor". This includes nil, so + that a frame which doesn't specify a cursor type gets a box, + which is the default in Emacs. */ + msdos_set_cursor_shape (f, 0, BOX_CURSOR_WIDTH); +} + +static void +IT_ring_bell (void) { if (visible_bell) { @@ -542,206 +868,1286 @@ IT_ring_bell () } } +/* Given a face id FACE, extract the face parameters to be used for + display until the face changes. The face parameters (actually, its + color) are used to construct the video attribute byte for each + glyph during the construction of the buffer that is then blitted to + the video RAM. */ static void IT_set_face (int face) { - struct face *fp; - extern struct face *intern_face (/* FRAME_PTR, struct face * */); + struct frame *sf = SELECTED_FRAME(); + struct face *fp = FACE_FROM_ID (sf, face); + struct face *dfp = FACE_FROM_ID (sf, DEFAULT_FACE_ID); + unsigned long fg, bg, dflt_fg, dflt_bg; - if (face == 1 || (face == 0 && highlight)) - fp = FRAME_MODE_LINE_FACE (foo); - else if (face <= 0 || face >= FRAME_N_COMPUTED_FACES (foo)) - fp = FRAME_DEFAULT_FACE (foo); - else - fp = intern_face (selected_frame, FRAME_COMPUTED_FACES (foo)[face]); - if (termscript) - fprintf (termscript, "", FACE_FOREGROUND (fp), FACE_BACKGROUND (fp)); + if (!fp) + { + fp = dfp; + /* The default face for the frame should always be realized and + cached. */ + if (!fp) + abort (); + } screen_face = face; - ScreenAttrib = (FACE_BACKGROUND (fp) << 4) | FACE_FOREGROUND (fp); -} - -static -IT_write_glyphs (GLYPH *str, int len) -{ - int newface; - int ch, l = len; - unsigned char *buf, *bp; + fg = fp->foreground; + bg = fp->background; + dflt_fg = dfp->foreground; + dflt_bg = dfp->background; + + /* Don't use invalid colors. In particular, FACE_TTY_DEFAULT_* + colors mean use the colors of the default face, except that if + highlight is on, invert the foreground and the background. Note + that we assume all 16 colors to be available for the background, + since Emacs switches on this mode (and loses the blinking + attribute) at startup. */ + if (fg == FACE_TTY_DEFAULT_COLOR || fg == FACE_TTY_DEFAULT_FG_COLOR) + fg = FRAME_FOREGROUND_PIXEL (sf); + else if (fg == FACE_TTY_DEFAULT_BG_COLOR) + fg = FRAME_BACKGROUND_PIXEL (sf); + if (bg == FACE_TTY_DEFAULT_COLOR || bg == FACE_TTY_DEFAULT_BG_COLOR) + bg = FRAME_BACKGROUND_PIXEL (sf); + else if (bg == FACE_TTY_DEFAULT_FG_COLOR) + bg = FRAME_FOREGROUND_PIXEL (sf); + + /* Make sure highlighted lines really stand out, come what may. */ + if ((highlight || fp->tty_reverse_p) + && (fg == dflt_fg && bg == dflt_bg)) + { + unsigned long tem = fg; - if (len == 0) return; - - buf = bp = alloca (len * 2); - - while (--l >= 0) - { - newface = FAST_GLYPH_FACE (*str); - if (newface != screen_face) - IT_set_face (newface); - ch = FAST_GLYPH_CHAR (*str); - *bp++ = (unsigned char)ch; - *bp++ = ScreenAttrib; - - if (termscript) - fputc (ch, termscript); - str++; + fg = bg; + bg = tem; } + /* If the user requested inverse video, obey. */ + if (inverse_video) + { + unsigned long tem2 = fg; - mouse_off_maybe (); - dosmemput (buf, 2 * len, - (int)ScreenPrimary + 2 * (new_pos_X + screen_size_X * new_pos_Y)); - new_pos_X += len; -} - -static -IT_clear_end_of_line (first_unused) -{ - char *spaces, *sp; - int i, j; - - IT_set_face (0); + fg = bg; + bg = tem2; + } if (termscript) - fprintf (termscript, ""); - i = (j = screen_size_X - new_pos_X) * 2; - spaces = sp = alloca (i); - - while (--j >= 0) + fprintf (termscript, "", face, + highlight ? "H" : "", fp->foreground, fp->background, fg, bg); + if (fg >= 0 && fg < 16) { - *sp++ = ' '; - *sp++ = ScreenAttrib; + ScreenAttrib &= 0xf0; + ScreenAttrib |= fg; + } + if (bg >= 0 && bg < 16) + { + ScreenAttrib &= 0x0f; + ScreenAttrib |= ((bg & 0x0f) << 4); } - - mouse_off_maybe (); - dosmemput (spaces, i, - (int)ScreenPrimary + 2 * (new_pos_X + screen_size_X * new_pos_Y)); } -static -IT_clear_screen (void) -{ - if (termscript) - fprintf (termscript, ""); - IT_set_face (0); - mouse_off (); - ScreenClear (); - new_pos_X = new_pos_Y = 0; -} +Lisp_Object Vdos_unsupported_char_glyph; -static -IT_clear_to_end (void) +static void +IT_write_glyphs (struct glyph *str, int str_len) { - if (termscript) - fprintf (termscript, ""); + unsigned char *screen_buf, *screen_bp, *screen_buf_end, *bp; + int unsupported_face = FAST_GLYPH_FACE (Vdos_unsupported_char_glyph); + unsigned unsupported_char= FAST_GLYPH_CHAR (Vdos_unsupported_char_glyph); + int offset = 2 * (new_pos_X + screen_size_X * new_pos_Y); + register int sl = str_len; + register int tlen = GLYPH_TABLE_LENGTH; + register Lisp_Object *tbase = GLYPH_TABLE_BASE; + + /* If terminal_coding does any conversion, use it, otherwise use + safe_terminal_coding. We can't use CODING_REQUIRE_ENCODING here + because it always returns 1 if terminal_coding.src_multibyte is 1. */ + struct coding_system *coding = + (terminal_coding.common_flags & CODING_REQUIRE_ENCODING_MASK + ? &terminal_coding + : &safe_terminal_coding); + struct frame *sf; + + /* Do we need to consider conversion of unibyte characters to + multibyte? */ + int convert_unibyte_characters + = (NILP (current_buffer->enable_multibyte_characters) + && unibyte_display_via_language_environment); + + unsigned char conversion_buffer[256]; + int conversion_buffer_size = sizeof conversion_buffer; + + if (str_len <= 0) return; + + screen_buf = screen_bp = alloca (str_len * 2); + screen_buf_end = screen_buf + str_len * 2; + sf = SELECTED_FRAME(); + + /* Since faces get cached and uncached behind our back, we can't + rely on their indices in the cache being consistent across + invocations. So always reset the screen face to the default + face of the frame, before writing glyphs, and let the glyphs + set the right face if it's different from the default. */ + IT_set_face (DEFAULT_FACE_ID); + + /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at + the tail. */ + terminal_coding.mode &= ~CODING_MODE_LAST_BLOCK; + while (sl) + { + int cf, chlen, enclen; + unsigned char workbuf[MAX_MULTIBYTE_LENGTH], *buf; + unsigned ch; + + /* Glyphs with GLYPH_MASK_PADDING bit set are actually there + only for the redisplay code to know how many columns does + this character occupy on the screen. Skip padding glyphs. */ + if (CHAR_GLYPH_PADDING_P (*str)) + { + str++; + sl--; + } + else + { + register GLYPH g = GLYPH_FROM_CHAR_GLYPH (*str); + int glyph_not_in_table = 0; - while (new_pos_Y < screen_size_Y) { - new_pos_X = 0; - IT_clear_end_of_line (0); - new_pos_Y++; - } -} + /* If g is negative, it means we have a multibyte character + in *str. That's what GLYPH_FROM_CHAR_GLYPH returns for + multibyte characters. */ + if (g < 0 || g >= tlen) + { + /* This glyph doesn't have an entry in Vglyph_table. */ + ch = str->u.ch; + glyph_not_in_table = 1; + } + else + { + /* This glyph has an entry in Vglyph_table, so process + any aliases before testing for simpleness. */ + GLYPH_FOLLOW_ALIASES (tbase, tlen, g); + ch = FAST_GLYPH_CHAR (g); + } -static -IT_cursor_to (int y, int x) -{ - if (termscript) - fprintf (termscript, "\n", x, y); - new_pos_X = x; - new_pos_Y = y; -} + /* Convert the character code to multibyte, if they + requested display via language environment. We only want + to convert unibyte characters to multibyte in unibyte + buffers! Otherwise, the 8-bit value in CH came from the + display table set up to display foreign characters. */ + if (SINGLE_BYTE_CHAR_P (ch) && convert_unibyte_characters + && (ch >= 0240 + || (ch >= 0200 && !NILP (Vnonascii_translation_table)))) + ch = unibyte_char_to_multibyte (ch); + + /* Invalid characters are displayed with a special glyph. */ + if (! CHAR_VALID_P (ch, 0)) + { + g = !NILP (Vdos_unsupported_char_glyph) + ? Vdos_unsupported_char_glyph + : MAKE_GLYPH (sf, '\177', GLYPH_FACE (sf, g)); + ch = FAST_GLYPH_CHAR (g); + } -static -IT_reassert_line_highlight (new, vpos) - int new, vpos; -{ - highlight = new; - IT_set_face (0); /* To possibly clear the highlighting. */ -} + /* If the face of this glyph is different from the current + screen face, update the screen attribute byte. */ + cf = str->face_id; + if (cf != screen_face) + IT_set_face (cf); /* handles invalid faces gracefully */ -static -IT_change_line_highlight (new_highlight, vpos, first_unused_hpos) -{ - highlight = new_highlight; - IT_set_face (0); /* To possibly clear the highlighting. */ - IT_cursor_to (vpos, 0); - IT_clear_end_of_line (first_unused_hpos); -} + if (glyph_not_in_table || GLYPH_SIMPLE_P (tbase, tlen, g)) + { + /* We generate the multi-byte form of CH in WORKBUF. */ + chlen = CHAR_STRING (ch, workbuf); + buf = workbuf; + } + else + { + /* We have a string in Vglyph_table. */ + chlen = GLYPH_LENGTH (tbase, g); + buf = GLYPH_STRING (tbase, g); + } -static -IT_update_begin () -{ - highlight = 0; - IT_set_face (0); /* To possibly clear the highlighting. */ - screen_face = -1; -} + /* If the character is not multibyte, don't bother converting it. */ + if (chlen == 1) + { + *conversion_buffer = (unsigned char)ch; + chlen = 0; + enclen = 1; + } + else + { + coding->src_multibyte = 1; + encode_coding (coding, buf, conversion_buffer, chlen, + conversion_buffer_size); + chlen -= coding->consumed; + enclen = coding->produced; + + /* Replace glyph codes that cannot be converted by + terminal_coding with Vdos_unsupported_char_glyph. */ + if (*conversion_buffer == '?') + { + unsigned char *cbp = conversion_buffer; -static -IT_update_end () -{ -} + while (cbp < conversion_buffer + enclen && *cbp == '?') + *cbp++ = unsupported_char; + if (unsupported_face != screen_face) + IT_set_face (unsupported_face); + } + } -/* This was more or less copied from xterm.c + if (enclen + chlen > screen_buf_end - screen_bp) + { + /* The allocated buffer for screen writes is too small. + Flush it and loop again without incrementing STR, so + that the next loop will begin with the same glyph. */ + int nbytes = screen_bp - screen_buf; + + mouse_off_maybe (); + dosmemput (screen_buf, nbytes, (int)ScreenPrimary + offset); + if (screen_virtual_segment) + dosv_refresh_virtual_screen (offset, nbytes / 2); + new_pos_X += nbytes / 2; + offset += nbytes; + + /* Prepare to reuse the same buffer again. */ + screen_bp = screen_buf; + } + else + { + /* There's enough place in the allocated buffer to add + the encoding of this glyph. */ - Nowadays, the corresponding function under X is `x_set_menu_bar_lines_1' - on xfns.c */ + /* First, copy the encoded bytes. */ + for (bp = conversion_buffer; enclen--; bp++) + { + *screen_bp++ = (unsigned char)*bp; + *screen_bp++ = ScreenAttrib; + if (termscript) + fputc (*bp, termscript); + } -static void -IT_set_menu_bar_lines (window, n) - Lisp_Object window; - int n; + /* Now copy the bytes not consumed by the encoding. */ + if (chlen > 0) + { + buf += coding->consumed; + while (chlen--) + { + if (termscript) + fputc (*buf, termscript); + *screen_bp++ = (unsigned char)*buf++; + *screen_bp++ = ScreenAttrib; + } + } + + /* Update STR and its remaining length. */ + str++; + sl--; + } + } + } + + /* Dump whatever is left in the screen buffer. */ + mouse_off_maybe (); + dosmemput (screen_buf, screen_bp - screen_buf, (int)ScreenPrimary + offset); + if (screen_virtual_segment) + dosv_refresh_virtual_screen (offset, (screen_bp - screen_buf) / 2); + new_pos_X += (screen_bp - screen_buf) / 2; + + /* We may have to output some codes to terminate the writing. */ + if (CODING_REQUIRE_FLUSHING (coding)) + { + coding->mode |= CODING_MODE_LAST_BLOCK; + encode_coding (coding, "", conversion_buffer, 0, conversion_buffer_size); + if (coding->produced > 0) + { + screen_buf = alloca (coding->produced * 2); + for (screen_bp = screen_buf, bp = conversion_buffer; + coding->produced--; bp++) + { + *screen_bp++ = (unsigned char)*bp; + *screen_bp++ = ScreenAttrib; + if (termscript) + fputc (*bp, termscript); + } + offset += screen_bp - screen_buf; + mouse_off_maybe (); + dosmemput (screen_buf, screen_bp - screen_buf, + (int)ScreenPrimary + offset); + if (screen_virtual_segment) + dosv_refresh_virtual_screen (offset, (screen_bp - screen_buf) / 2); + new_pos_X += (screen_bp - screen_buf) / 2; + } + } +} + +/************************************************************************ + Mouse Highlight (and friends..) + ************************************************************************/ + +/* This is used for debugging, to turn off note_mouse_highlight. */ +int disable_mouse_highlight; + +/* If non-nil, dos_rawgetc generates an event to display that string. + (The display is done in keyboard.c:read_char, by calling + show_help_echo.) */ +static Lisp_Object help_echo; +static Lisp_Object previous_help_echo; /* a helper temporary variable */ + +/* These record the window, the object and the position where the help + echo string was generated. */ +static Lisp_Object help_echo_window; +static Lisp_Object help_echo_object; +static int help_echo_pos; + +static int mouse_preempted = 0; /* non-zero when XMenu gobbles mouse events */ + +/* Set the mouse pointer shape according to whether it is in the + area where the mouse highlight is in effect. */ +static void +IT_set_mouse_pointer (int mode) { - struct window *w = XWINDOW (window); + /* A no-op for now. DOS text-mode mouse pointer doesn't offer too + many possibilities to change its shape, and the available + functionality pretty much sucks (e.g., almost every reasonable + shape will conceal the character it is on). Since the color of + the pointer changes in the highlighted area, it is not clear to + me whether anything else is required, anyway. */ +} - XSETFASTINT (w->last_modified, 0); - XSETFASTINT (w->last_overlay_modified, 0); - XSETFASTINT (w->top, XFASTINT (w->top) + n); - XSETFASTINT (w->height, XFASTINT (w->height) - n); +/* Display the active region described by mouse_face_* + in its mouse-face if HL > 0, in its normal face if HL = 0. */ +static void +show_mouse_face (struct display_info *dpyinfo, int hl) +{ + struct window *w = XWINDOW (dpyinfo->mouse_face_window); + struct frame *f = XFRAME (WINDOW_FRAME (w)); + int i; + struct face *fp; - /* Handle just the top child in a vertical split. */ - if (!NILP (w->vchild)) - IT_set_menu_bar_lines (w->vchild, n); + + /* If window is in the process of being destroyed, don't bother + doing anything. */ + if (w->current_matrix == NULL) + goto set_cursor_shape; + + /* Recognize when we are called to operate on rows that don't exist + anymore. This can happen when a window is split. */ + if (dpyinfo->mouse_face_end_row >= w->current_matrix->nrows) + goto set_cursor_shape; + + /* There's no sense to do anything if the mouse face isn't realized. */ + if (hl > 0) + { + fp = FACE_FROM_ID (SELECTED_FRAME(), dpyinfo->mouse_face_face_id); + if (!fp) + goto set_cursor_shape; + } - /* Adjust all children in a horizontal split. */ - for (window = w->hchild; !NILP (window); window = w->next) + /* Note that mouse_face_beg_row etc. are window relative. */ + for (i = dpyinfo->mouse_face_beg_row; + i <= dpyinfo->mouse_face_end_row; + i++) { - w = XWINDOW (window); - IT_set_menu_bar_lines (window, n); + int start_hpos, end_hpos; + struct glyph_row *row = MATRIX_ROW (w->current_matrix, i); + + /* Don't do anything if row doesn't have valid contents. */ + if (!row->enabled_p) + continue; + + /* For all but the first row, the highlight starts at column 0. */ + if (i == dpyinfo->mouse_face_beg_row) + start_hpos = dpyinfo->mouse_face_beg_col; + else + start_hpos = 0; + + if (i == dpyinfo->mouse_face_end_row) + end_hpos = dpyinfo->mouse_face_end_col; + else + end_hpos = row->used[TEXT_AREA]; + + if (end_hpos <= start_hpos) + continue; + /* Record that some glyphs of this row are displayed in + mouse-face. */ + row->mouse_face_p = hl > 0; + if (hl > 0) + { + int vpos = row->y + WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w); + int kstart = start_hpos + WINDOW_DISPLAY_LEFT_EDGE_PIXEL_X (w); + int nglyphs = end_hpos - start_hpos; + int offset = ScreenPrimary + 2*(vpos*screen_size_X + kstart) + 1; + int start_offset = offset; + + if (termscript) + fprintf (termscript, "\n", + kstart, kstart + nglyphs - 1, vpos); + + mouse_off (); + IT_set_face (dpyinfo->mouse_face_face_id); + /* Since we are going to change only the _colors_ of the + displayed text, there's no need to go through all the + pain of generating and encoding the text from the glyphs. + Instead, we simply poke the attribute byte of each + affected position in video memory with the colors + computed by IT_set_face! */ + _farsetsel (_dos_ds); + while (nglyphs--) + { + _farnspokeb (offset, ScreenAttrib); + offset += 2; + } + if (screen_virtual_segment) + dosv_refresh_virtual_screen (start_offset, end_hpos - start_hpos); + mouse_on (); + } + else + { + /* We are removing a previously-drawn mouse highlight. The + safest way to do so is to redraw the glyphs anew, since + all kinds of faces and display tables could have changed + behind our back. */ + int nglyphs = end_hpos - start_hpos; + int save_x = new_pos_X, save_y = new_pos_Y; + + if (end_hpos >= row->used[TEXT_AREA]) + nglyphs = row->used[TEXT_AREA] - start_hpos; + + /* IT_write_glyphs writes at cursor position, so we need to + temporarily move cursor coordinates to the beginning of + the highlight region. */ + new_pos_X = start_hpos + WINDOW_DISPLAY_LEFT_EDGE_PIXEL_X (w); + new_pos_Y = row->y + WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w); + + if (termscript) + fprintf (termscript, "", + new_pos_X, new_pos_X + nglyphs - 1, new_pos_Y); + IT_write_glyphs (row->glyphs[TEXT_AREA] + start_hpos, nglyphs); + if (termscript) + fputs ("\n", termscript); + new_pos_X = save_x; + new_pos_Y = save_y; + } } + + set_cursor_shape: + + /* Change the mouse pointer shape. */ + IT_set_mouse_pointer (hl); } -/* This was copied from xfns.c */ +/* Clear out the mouse-highlighted active region. + Redraw it un-highlighted first. */ +static void +clear_mouse_face (struct display_info *dpyinfo) +{ + if (! NILP (dpyinfo->mouse_face_window)) + show_mouse_face (dpyinfo, 0); -void -x_set_menu_bar_lines (f, value, oldval) - struct frame *f; - Lisp_Object value, oldval; + dpyinfo->mouse_face_beg_row = dpyinfo->mouse_face_beg_col = -1; + dpyinfo->mouse_face_end_row = dpyinfo->mouse_face_end_col = -1; + dpyinfo->mouse_face_window = Qnil; +} + +/* Find the glyph matrix position of buffer position POS in window W. + *HPOS and *VPOS are set to the positions found. W's current glyphs + must be up to date. If POS is above window start return (0, 0). + If POS is after end of W, return end of last line in W. */ +static int +fast_find_position (struct window *w, int pos, int *hpos, int *vpos) +{ + int i; + int lastcol; + int maybe_next_line_p = 0; + int line_start_position; + int yb = window_text_bottom_y (w); + struct glyph_row *row = MATRIX_ROW (w->current_matrix, 0); + struct glyph_row *best_row = row; + + while (row->y < yb) + { + if (row->used[TEXT_AREA]) + line_start_position = row->glyphs[TEXT_AREA]->charpos; + else + line_start_position = 0; + + if (line_start_position > pos) + break; + /* If the position sought is the end of the buffer, + don't include the blank lines at the bottom of the window. */ + else if (line_start_position == pos + && pos == BUF_ZV (XBUFFER (w->buffer))) + { + maybe_next_line_p = 1; + break; + } + else if (line_start_position > 0) + best_row = row; + + /* Don't overstep the last matrix row, lest we get into the + never-never land... */ + if (row->y + 1 >= yb) + break; + + ++row; + } + + /* Find the right column within BEST_ROW. */ + lastcol = 0; + row = best_row; + for (i = 0; i < row->used[TEXT_AREA]; i++) + { + struct glyph *glyph = row->glyphs[TEXT_AREA] + i; + int charpos; + + charpos = glyph->charpos; + if (charpos == pos) + { + *hpos = i; + *vpos = row->y; + return 1; + } + else if (charpos > pos) + break; + else if (charpos > 0) + lastcol = i; + } + + /* If we're looking for the end of the buffer, + and we didn't find it in the line we scanned, + use the start of the following line. */ + if (maybe_next_line_p) + { + ++row; + lastcol = 0; + } + + *vpos = row->y; + *hpos = lastcol + 1; + return 0; +} + +/* Take proper action when mouse has moved to the mode or top line of + window W, x-position X. MODE_LINE_P non-zero means mouse is on the + mode line. X is relative to the start of the text display area of + W, so the width of bitmap areas and scroll bars must be subtracted + to get a position relative to the start of the mode line. */ +static void +IT_note_mode_line_highlight (struct window *w, int x, int mode_line_p) +{ + struct frame *f = XFRAME (w->frame); + struct display_info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + struct glyph_row *row; + + if (mode_line_p) + row = MATRIX_MODE_LINE_ROW (w->current_matrix); + else + row = MATRIX_HEADER_LINE_ROW (w->current_matrix); + + if (row->enabled_p) + { + extern Lisp_Object Qhelp_echo; + struct glyph *glyph, *end; + Lisp_Object help, map; + + /* Find the glyph under X. */ + glyph = row->glyphs[TEXT_AREA] + + x - FRAME_LEFT_SCROLL_BAR_WIDTH (f) * CANON_X_UNIT (f); + end = glyph + row->used[TEXT_AREA]; + if (glyph < end + && STRINGP (glyph->object) + && XSTRING (glyph->object)->intervals + && glyph->charpos >= 0 + && glyph->charpos < XSTRING (glyph->object)->size) + { + /* If we're on a string with `help-echo' text property, + arrange for the help to be displayed. This is done by + setting the global variable help_echo to the help string. */ + help = Fget_text_property (make_number (glyph->charpos), + Qhelp_echo, glyph->object); + if (!NILP (help)) + { + help_echo = help; + XSETWINDOW (help_echo_window, w); + help_echo_object = glyph->object; + help_echo_pos = glyph->charpos; + } + } + } +} + +/* Take proper action when the mouse has moved to position X, Y on + frame F as regards highlighting characters that have mouse-face + properties. Also de-highlighting chars where the mouse was before. + X and Y can be negative or out of range. */ +static void +IT_note_mouse_highlight (struct frame *f, int x, int y) +{ + struct display_info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + int portion = -1; + Lisp_Object window; + struct window *w; + + /* When a menu is active, don't highlight because this looks odd. */ + if (mouse_preempted) + return; + + if (disable_mouse_highlight + || !f->glyphs_initialized_p) + return; + + dpyinfo->mouse_face_mouse_x = x; + dpyinfo->mouse_face_mouse_y = y; + dpyinfo->mouse_face_mouse_frame = f; + + if (dpyinfo->mouse_face_defer) + return; + + if (gc_in_progress) + { + dpyinfo->mouse_face_deferred_gc = 1; + return; + } + + /* Which window is that in? */ + window = window_from_coordinates (f, x, y, &portion, 0); + + /* If we were displaying active text in another window, clear that. */ + if (! EQ (window, dpyinfo->mouse_face_window)) + clear_mouse_face (dpyinfo); + + /* Not on a window -> return. */ + if (!WINDOWP (window)) + return; + + /* Convert to window-relative coordinates. */ + w = XWINDOW (window); + x -= WINDOW_DISPLAY_LEFT_EDGE_PIXEL_X (w); + y -= WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w); + + if (portion == 1 || portion == 3) + { + /* Mouse is on the mode or top line. */ + IT_note_mode_line_highlight (w, x, portion == 1); + return; + } + else + IT_set_mouse_pointer (0); + + /* Are we in a window whose display is up to date? + And verify the buffer's text has not changed. */ + if (/* Within text portion of the window. */ + portion == 0 + && EQ (w->window_end_valid, w->buffer) + && XFASTINT (w->last_modified) == BUF_MODIFF (XBUFFER (w->buffer)) + && (XFASTINT (w->last_overlay_modified) + == BUF_OVERLAY_MODIFF (XBUFFER (w->buffer)))) + { + int pos, i; + struct glyph_row *row; + struct glyph *glyph; + int nrows = w->current_matrix->nrows; + + /* Find the glyph under X/Y. */ + glyph = NULL; + if (y >= 0 && y < nrows) + { + row = MATRIX_ROW (w->current_matrix, y); + /* Give up if some row before the one we are looking for is + not enabled. */ + for (i = 0; i <= y; i++) + if (!MATRIX_ROW (w->current_matrix, i)->enabled_p) + break; + if (i > y /* all rows upto and including the one at Y are enabled */ + && row->displays_text_p + && x < window_box_width (w, TEXT_AREA)) + { + glyph = row->glyphs[TEXT_AREA]; + if (x >= row->used[TEXT_AREA]) + glyph = NULL; + else + { + glyph += x; + if (!BUFFERP (glyph->object)) + glyph = NULL; + } + } + } + + /* Clear mouse face if X/Y not over text. */ + if (glyph == NULL) + { + clear_mouse_face (dpyinfo); + return; + } + + if (!BUFFERP (glyph->object)) + abort (); + pos = glyph->charpos; + + /* Check for mouse-face and help-echo. */ + { + extern Lisp_Object Qmouse_face; + Lisp_Object mouse_face, overlay, position; + Lisp_Object *overlay_vec; + int len, noverlays; + struct buffer *obuf; + int obegv, ozv; + + /* If we get an out-of-range value, return now; avoid an error. */ + if (pos > BUF_Z (XBUFFER (w->buffer))) + return; + + /* Make the window's buffer temporarily current for + overlays_at and compute_char_face. */ + obuf = current_buffer; + current_buffer = XBUFFER (w->buffer); + obegv = BEGV; + ozv = ZV; + BEGV = BEG; + ZV = Z; + + /* Is this char mouse-active or does it have help-echo? */ + XSETINT (position, pos); + + /* Put all the overlays we want in a vector in overlay_vec. + Store the length in len. If there are more than 10, make + enough space for all, and try again. */ + len = 10; + overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object)); + noverlays = overlays_at (pos, 0, &overlay_vec, &len, NULL, NULL, 0); + if (noverlays > len) + { + len = noverlays; + overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object)); + noverlays = overlays_at (pos, + 0, &overlay_vec, &len, NULL, NULL, 0); + } + + /* Sort overlays into increasing priority order. */ + noverlays = sort_overlays (overlay_vec, noverlays, w); + + /* Check mouse-face highlighting. */ + if (! (EQ (window, dpyinfo->mouse_face_window) + && y >= dpyinfo->mouse_face_beg_row + && y <= dpyinfo->mouse_face_end_row + && (y > dpyinfo->mouse_face_beg_row + || x >= dpyinfo->mouse_face_beg_col) + && (y < dpyinfo->mouse_face_end_row + || x < dpyinfo->mouse_face_end_col + || dpyinfo->mouse_face_past_end))) + { + /* Clear the display of the old active region, if any. */ + clear_mouse_face (dpyinfo); + + /* Find highest priority overlay that has a mouse-face prop. */ + overlay = Qnil; + for (i = noverlays - 1; i >= 0; --i) + { + mouse_face = Foverlay_get (overlay_vec[i], Qmouse_face); + if (!NILP (mouse_face)) + { + overlay = overlay_vec[i]; + break; + } + } + + /* If no overlay applies, get a text property. */ + if (NILP (overlay)) + mouse_face = Fget_text_property (position, Qmouse_face, + w->buffer); + + /* Handle the overlay case. */ + if (! NILP (overlay)) + { + /* Find the range of text around this char that + should be active. */ + Lisp_Object before, after; + int ignore; + + before = Foverlay_start (overlay); + after = Foverlay_end (overlay); + /* Record this as the current active region. */ + fast_find_position (w, XFASTINT (before), + &dpyinfo->mouse_face_beg_col, + &dpyinfo->mouse_face_beg_row); + dpyinfo->mouse_face_past_end + = !fast_find_position (w, XFASTINT (after), + &dpyinfo->mouse_face_end_col, + &dpyinfo->mouse_face_end_row); + dpyinfo->mouse_face_window = window; + dpyinfo->mouse_face_face_id + = face_at_buffer_position (w, pos, 0, 0, + &ignore, pos + 1, 1); + + /* Display it as active. */ + show_mouse_face (dpyinfo, 1); + } + /* Handle the text property case. */ + else if (! NILP (mouse_face)) + { + /* Find the range of text around this char that + should be active. */ + Lisp_Object before, after, beginning, end; + int ignore; + + beginning = Fmarker_position (w->start); + XSETINT (end, (BUF_Z (XBUFFER (w->buffer)) + - XFASTINT (w->window_end_pos))); + before + = Fprevious_single_property_change (make_number (pos + 1), + Qmouse_face, + w->buffer, beginning); + after + = Fnext_single_property_change (position, Qmouse_face, + w->buffer, end); + /* Record this as the current active region. */ + fast_find_position (w, XFASTINT (before), + &dpyinfo->mouse_face_beg_col, + &dpyinfo->mouse_face_beg_row); + dpyinfo->mouse_face_past_end + = !fast_find_position (w, XFASTINT (after), + &dpyinfo->mouse_face_end_col, + &dpyinfo->mouse_face_end_row); + dpyinfo->mouse_face_window = window; + dpyinfo->mouse_face_face_id + = face_at_buffer_position (w, pos, 0, 0, + &ignore, pos + 1, 1); + + /* Display it as active. */ + show_mouse_face (dpyinfo, 1); + } + } + + /* Look for a `help-echo' property. */ + { + Lisp_Object help; + extern Lisp_Object Qhelp_echo; + + /* Check overlays first. */ + help = Qnil; + for (i = noverlays - 1; i >= 0 && NILP (help); --i) + { + overlay = overlay_vec[i]; + help = Foverlay_get (overlay, Qhelp_echo); + } + + if (!NILP (help)) + { + help_echo = help; + help_echo_window = window; + help_echo_object = overlay; + help_echo_pos = pos; + } + /* Try text properties. */ + else if (NILP (help) + && ((STRINGP (glyph->object) + && glyph->charpos >= 0 + && glyph->charpos < XSTRING (glyph->object)->size) + || (BUFFERP (glyph->object) + && glyph->charpos >= BEGV + && glyph->charpos < ZV))) + { + help = Fget_text_property (make_number (glyph->charpos), + Qhelp_echo, glyph->object); + if (!NILP (help)) + { + help_echo = help; + help_echo_window = window; + help_echo_object = glyph->object; + help_echo_pos = glyph->charpos; + } + } + } + + BEGV = obegv; + ZV = ozv; + current_buffer = obuf; + } + } +} + +static void +IT_clear_end_of_line (int first_unused) +{ + char *spaces, *sp; + int i, j; + int offset = 2 * (new_pos_X + screen_size_X * new_pos_Y); + extern int fatal_error_in_progress; + + if (new_pos_X >= first_unused || fatal_error_in_progress) + return; + + IT_set_face (0); + i = (j = first_unused - new_pos_X) * 2; + if (termscript) + fprintf (termscript, "", new_pos_X, first_unused); + spaces = sp = alloca (i); + + while (--j >= 0) + { + *sp++ = ' '; + *sp++ = ScreenAttrib; + } + + mouse_off_maybe (); + dosmemput (spaces, i, (int)ScreenPrimary + offset); + if (screen_virtual_segment) + dosv_refresh_virtual_screen (offset, i / 2); + + /* clear_end_of_line_raw on term.c leaves the cursor at first_unused. + Let's follow their lead, in case someone relies on this. */ + new_pos_X = first_unused; +} + +static void +IT_clear_screen (void) { - int nlines; - int olines = FRAME_MENU_BAR_LINES (f); + if (termscript) + fprintf (termscript, ""); + IT_set_face (0); + mouse_off (); + ScreenClear (); + if (screen_virtual_segment) + dosv_refresh_virtual_screen (0, screen_size); + new_pos_X = new_pos_Y = 0; +} + +static void +IT_clear_to_end (void) +{ + if (termscript) + fprintf (termscript, ""); + + while (new_pos_Y < screen_size_Y) { + new_pos_X = 0; + IT_clear_end_of_line (screen_size_X); + new_pos_Y++; + } +} + +static void +IT_cursor_to (int y, int x) +{ + if (termscript) + fprintf (termscript, "\n", x, y); + new_pos_X = x; + new_pos_Y = y; +} + +static int cursor_cleared; + +static void +IT_display_cursor (int on) +{ + if (on && cursor_cleared) + { + ScreenSetCursor (current_pos_Y, current_pos_X); + cursor_cleared = 0; + } + else if (!on && !cursor_cleared) + { + ScreenSetCursor (-1, -1); + cursor_cleared = 1; + } +} + +/* Emacs calls cursor-movement functions a lot when it updates the + display (probably a legacy of old terminals where you cannot + update a screen line without first moving the cursor there). + However, cursor movement is expensive on MSDOS (it calls a slow + BIOS function and requires 2 mode switches), while actual screen + updates access the video memory directly and don't depend on + cursor position. To avoid slowing down the redisplay, we cheat: + all functions that move the cursor only set internal variables + which record the cursor position, whereas the cursor is only + moved to its final position whenever screen update is complete. + + `IT_cmgoto' is called from the keyboard reading loop and when the + frame update is complete. This means that we are ready for user + input, so we update the cursor position to show where the point is, + and also make the mouse pointer visible. + + Special treatment is required when the cursor is in the echo area, + to put the cursor at the end of the text displayed there. */ + +static void +IT_cmgoto (FRAME_PTR f) +{ + /* Only set the cursor to where it should be if the display is + already in sync with the window contents. */ + int update_cursor_pos = 1; /* MODIFF == unchanged_modified; */ + + /* FIXME: This needs to be rewritten for the new redisplay, or + removed. */ +#if 0 + static int previous_pos_X = -1; + + update_cursor_pos = 1; /* temporary!!! */ + + /* If the display is in sync, forget any previous knowledge about + cursor position. This is primarily for unexpected events like + C-g in the minibuffer. */ + if (update_cursor_pos && previous_pos_X >= 0) + previous_pos_X = -1; + /* If we are in the echo area, put the cursor at the + end of the echo area message. */ + if (!update_cursor_pos + && XFASTINT (XWINDOW (FRAME_MINIBUF_WINDOW (f))->top) <= new_pos_Y) + { + int tem_X = current_pos_X, dummy; + + if (echo_area_glyphs) + { + tem_X = echo_area_glyphs_length; + /* Save current cursor position, to be restored after the + echo area message is erased. Only remember one level + of previous cursor position. */ + if (previous_pos_X == -1) + ScreenGetCursor (&dummy, &previous_pos_X); + } + else if (previous_pos_X >= 0) + { + /* We wind up here after the echo area message is erased. + Restore the cursor position we remembered above. */ + tem_X = previous_pos_X; + previous_pos_X = -1; + } + + if (current_pos_X != tem_X) + { + new_pos_X = tem_X; + update_cursor_pos = 1; + } + } +#endif + + if (update_cursor_pos + && (current_pos_X != new_pos_X || current_pos_Y != new_pos_Y)) + { + ScreenSetCursor (current_pos_Y = new_pos_Y, current_pos_X = new_pos_X); + if (termscript) + fprintf (termscript, "\n", current_pos_X, current_pos_Y); + } + + /* Maybe cursor is invisible, so make it visible. */ + IT_display_cursor (1); + + /* Mouse pointer should be always visible if we are waiting for + keyboard input. */ + if (!mouse_visible) + mouse_on (); +} + +static void +IT_reassert_line_highlight (int new, int vpos) +{ + highlight = new; +} + +static void +IT_change_line_highlight (int new_highlight, int y, int vpos, int first_unused_hpos) +{ + highlight = new_highlight; + IT_cursor_to (vpos, 0); + IT_clear_end_of_line (first_unused_hpos); +} + +static void +IT_update_begin (struct frame *f) +{ + struct display_info *display_info = FRAME_X_DISPLAY_INFO (f); + struct frame *mouse_face_frame = display_info->mouse_face_mouse_frame; + + highlight = 0; + + BLOCK_INPUT; + + if (f && f == mouse_face_frame) + { + /* Don't do highlighting for mouse motion during the update. */ + display_info->mouse_face_defer = 1; + + /* If F needs to be redrawn, simply forget about any prior mouse + highlighting. */ + if (FRAME_GARBAGED_P (f)) + display_info->mouse_face_window = Qnil; + + /* Can we tell that this update does not affect the window + where the mouse highlight is? If so, no need to turn off. + Likewise, don't do anything if none of the enabled rows + contains glyphs highlighted in mouse face. */ + if (!NILP (display_info->mouse_face_window) + && WINDOWP (display_info->mouse_face_window)) + { + struct window *w = XWINDOW (display_info->mouse_face_window); + int i; + + /* If the mouse highlight is in the window that was deleted + (e.g., if it was popped by completion), clear highlight + unconditionally. */ + if (NILP (w->buffer)) + display_info->mouse_face_window = Qnil; + else + { + for (i = 0; i < w->desired_matrix->nrows; ++i) + if (MATRIX_ROW_ENABLED_P (w->desired_matrix, i) + && MATRIX_ROW (w->current_matrix, i)->mouse_face_p) + break; + } + + if (NILP (w->buffer) || i < w->desired_matrix->nrows) + clear_mouse_face (display_info); + } + } + else if (mouse_face_frame && !FRAME_LIVE_P (mouse_face_frame)) + { + /* If the frame with mouse highlight was deleted, invalidate the + highlight info. */ + display_info->mouse_face_beg_row = display_info->mouse_face_beg_col = -1; + display_info->mouse_face_end_row = display_info->mouse_face_end_col = -1; + display_info->mouse_face_window = Qnil; + display_info->mouse_face_deferred_gc = 0; + display_info->mouse_face_mouse_frame = NULL; + } + + UNBLOCK_INPUT; +} + +static void +IT_update_end (struct frame *f) +{ + highlight = 0; + FRAME_X_DISPLAY_INFO (f)->mouse_face_defer = 0; +} - /* Right now, menu bars don't work properly in minibuf-only frames; - most of the commands try to apply themselves to the minibuffer - frame itslef, and get an error because you can't switch buffers - in or split the minibuffer window. */ - if (FRAME_MINIBUF_ONLY_P (f)) +Lisp_Object Qcursor_type; + +static void +IT_frame_up_to_date (struct frame *f) +{ + struct display_info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Lisp_Object new_cursor, frame_desired_cursor; + struct window *sw; + + if (dpyinfo->mouse_face_deferred_gc + || (f && f == dpyinfo->mouse_face_mouse_frame)) + { + BLOCK_INPUT; + if (dpyinfo->mouse_face_mouse_frame) + IT_note_mouse_highlight (dpyinfo->mouse_face_mouse_frame, + dpyinfo->mouse_face_mouse_x, + dpyinfo->mouse_face_mouse_y); + dpyinfo->mouse_face_deferred_gc = 0; + UNBLOCK_INPUT; + } + + /* Set the cursor type to whatever they wanted. In a minibuffer + window, we want the cursor to appear only if we are reading input + from this window, and we want the cursor to be taken from the + frame parameters. For the selected window, we use either its + buffer-local value or the value from the frame parameters if the + buffer doesn't define its local value for the cursor type. */ + sw = XWINDOW (f->selected_window); + frame_desired_cursor = Fcdr (Fassq (Qcursor_type, f->param_alist)); + if (cursor_in_echo_area + && FRAME_HAS_MINIBUF_P (f) + && EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window) + && sw == XWINDOW (echo_area_window)) + new_cursor = frame_desired_cursor; + else + { + struct buffer *b = XBUFFER (sw->buffer); + + if (EQ (b->cursor_type, Qt)) + new_cursor = frame_desired_cursor; + else if (NILP (b->cursor_type)) /* nil means no cursor */ + new_cursor = Fcons (Qbar, make_number (0)); + else + new_cursor = b->cursor_type; + } + + IT_set_cursor_type (f, new_cursor); + + IT_cmgoto (f); /* position cursor when update is done */ +} + +/* Copy LEN glyphs displayed on a single line whose vertical position + is YPOS, beginning at horizontal position XFROM to horizontal + position XTO, by moving blocks in the video memory. Used by + functions that insert and delete glyphs. */ +static void +IT_copy_glyphs (int xfrom, int xto, size_t len, int ypos) +{ + /* The offsets of source and destination relative to the + conventional memorty selector. */ + int from = 2 * (xfrom + screen_size_X * ypos) + ScreenPrimary; + int to = 2 * (xto + screen_size_X * ypos) + ScreenPrimary; + + if (from == to || len <= 0) return; - if (INTEGERP (value)) - nlines = XINT (value); + _farsetsel (_dos_ds); + + /* The source and destination might overlap, so we need to move + glyphs non-destructively. */ + if (from > to) + { + for ( ; len; from += 2, to += 2, len--) + _farnspokew (to, _farnspeekw (from)); + } else - nlines = 0; + { + from += (len - 1) * 2; + to += (len - 1) * 2; + for ( ; len; from -= 2, to -= 2, len--) + _farnspokew (to, _farnspeekw (from)); + } + if (screen_virtual_segment) + dosv_refresh_virtual_screen (ypos * screen_size_X * 2, screen_size_X); +} + +/* Insert and delete glyphs. */ +static void +IT_insert_glyphs (start, len) + register struct glyph *start; + register int len; +{ + int shift_by_width = screen_size_X - (new_pos_X + len); + + /* Shift right the glyphs from the nominal cursor position to the + end of this line. */ + IT_copy_glyphs (new_pos_X, new_pos_X + len, shift_by_width, new_pos_Y); + + /* Now write the glyphs to be inserted. */ + IT_write_glyphs (start, len); +} + +static void +IT_delete_glyphs (n) + register int n; +{ + abort (); +} - FRAME_MENU_BAR_LINES (f) = nlines; - IT_set_menu_bar_lines (f->root_window, nlines - olines); +/* set-window-configuration on window.c needs this. */ +void +x_set_menu_bar_lines (f, value, oldval) + struct frame *f; + Lisp_Object value, oldval; +{ + set_menu_bar_lines (f, value, oldval); } +/* This was copied from xfaces.c */ + +extern Lisp_Object Qbackground_color; +extern Lisp_Object Qforeground_color; +Lisp_Object Qreverse; +extern Lisp_Object Qtitle; + /* IT_set_terminal_modes is called when emacs is started, resumed, and whenever the screen is redrawn! */ -static +static void IT_set_terminal_modes (void) { - char *colors; - FRAME_PTR f; - struct face *fp; - if (termscript) fprintf (termscript, "\n"); highlight = 0; @@ -761,12 +2167,45 @@ IT_set_terminal_modes (void) startup_screen_size_Y = screen_size_Y; startup_screen_attrib = ScreenAttrib; +#if __DJGPP__ > 1 + /* Is DOS/V (or any other RSIS software which relocates + the screen) installed? */ + { + unsigned short es_value; + __dpmi_regs regs; + + regs.h.ah = 0xfe; /* get relocated screen address */ + if (ScreenPrimary == 0xb0000UL || ScreenPrimary == 0xb8000UL) + regs.x.es = (ScreenPrimary >> 4) & 0xffff; + else if (screen_old_address) /* already switched to Japanese mode once */ + regs.x.es = (screen_old_address >> 4) & 0xffff; + else + regs.x.es = ScreenMode () == 7 ? 0xb000 : 0xb800; + regs.x.di = 0; + es_value = regs.x.es; + __dpmi_int (0x10, ®s); + + if (regs.x.es != es_value) + { + /* screen_old_address is only set if ScreenPrimary does NOT + already point to the relocated buffer address returned by + the Int 10h/AX=FEh call above. DJGPP v2.02 and later sets + ScreenPrimary to that address at startup under DOS/V. */ + if (regs.x.es != (ScreenPrimary >> 4) & 0xffff) + screen_old_address = ScreenPrimary; + screen_virtual_segment = regs.x.es; + screen_virtual_offset = regs.x.di; + ScreenPrimary = (screen_virtual_segment << 4) + screen_virtual_offset; + } + } +#endif /* __DJGPP__ > 1 */ + ScreenGetCursor (&startup_pos_Y, &startup_pos_X); ScreenRetrieve (startup_screen_buffer = xmalloc (screen_size * 2)); if (termscript) fprintf (termscript, "\n", - screen_size_X, screen_size_Y); + screen_size_X, screen_size_Y); bright_bg (); } @@ -774,7 +2213,7 @@ IT_set_terminal_modes (void) /* IT_reset_terminal_modes is called when emacs is suspended or killed. */ -static +static void IT_reset_terminal_modes (void) { int display_row_start = (int) ScreenPrimary; @@ -795,7 +2234,11 @@ IT_reset_terminal_modes (void) return; mouse_off (); - + + /* Leave the video system in the same state as we found it, + as far as the blink/bright-background bit is concerned. */ + maybe_enable_blinking (); + /* We have a situation here. We cannot just do ScreenUpdate(startup_screen_buffer) because the luser could have changed screen dimensions inside Emacs @@ -809,22 +2252,34 @@ IT_reset_terminal_modes (void) is also restored within the visible dimensions. */ ScreenAttrib = startup_screen_attrib; - ScreenClear (); - if (update_row_len > saved_row_len) - update_row_len = saved_row_len; - if (current_rows > startup_screen_size_Y) - current_rows = startup_screen_size_Y; + /* Don't restore the screen if we are exiting less than 2 seconds + after startup: we might be crashing, and the screen might show + some vital clues to what's wrong. */ + if (clock () - startup_time >= 2*CLOCKS_PER_SEC) + { + ScreenClear (); + if (screen_virtual_segment) + dosv_refresh_virtual_screen (0, screen_size); - if (termscript) - fprintf (termscript, "\n", - update_row_len / 2, current_rows); + if (update_row_len > saved_row_len) + update_row_len = saved_row_len; + if (current_rows > startup_screen_size_Y) + current_rows = startup_screen_size_Y; - while (current_rows--) - { - dosmemput (saved_row, update_row_len, display_row_start); - saved_row += saved_row_len; - display_row_start += to_next_row; + if (termscript) + fprintf (termscript, "\n", + update_row_len / 2, current_rows); + + while (current_rows--) + { + dosmemput (saved_row, update_row_len, display_row_start); + if (screen_virtual_segment) + dosv_refresh_virtual_screen (display_row_start - ScreenPrimary, + update_row_len / 2); + saved_row += saved_row_len; + display_row_start += to_next_row; + } } if (startup_pos_X < cursor_pos_X) cursor_pos_X = startup_pos_X; @@ -837,64 +2292,237 @@ IT_reset_terminal_modes (void) term_setup_done = 0; } -static -IT_set_terminal_window (void) +static void +IT_set_terminal_window (int foo) +{ +} + +/* Remember the screen colors of the curent frame, to serve as the + default colors for newly-created frames. */ + +static int initial_screen_colors[2]; + +DEFUN ("msdos-remember-default-colors", Fmsdos_remember_default_colors, + Smsdos_remember_default_colors, 1, 1, 0, + "Remember the screen colors of the current frame.") + (frame) + Lisp_Object frame; { + struct frame *f; + + CHECK_FRAME (frame, 0); + f= XFRAME (frame); + + /* This function is called after applying default-frame-alist to the + initial frame. At that time, if reverse-colors option was + specified in default-frame-alist, it was already applied, and + frame colors are reversed. We need to account for that. */ + if (EQ (Fcdr (Fassq (Qreverse, f->param_alist)), Qt)) + { + initial_screen_colors[0] = FRAME_BACKGROUND_PIXEL (f); + initial_screen_colors[1] = FRAME_FOREGROUND_PIXEL (f); + } + else + { + initial_screen_colors[0] = FRAME_FOREGROUND_PIXEL (f); + initial_screen_colors[1] = FRAME_BACKGROUND_PIXEL (f); + } } void IT_set_frame_parameters (f, alist) - FRAME_PTR f; + struct frame *f; Lisp_Object alist; { Lisp_Object tail; - int redraw; - extern unsigned long load_color (); + int length = XINT (Flength (alist)); + int i, j; + Lisp_Object *parms + = (Lisp_Object *) alloca (length * sizeof (Lisp_Object)); + Lisp_Object *values + = (Lisp_Object *) alloca (length * sizeof (Lisp_Object)); + /* Do we have to reverse the foreground and background colors? */ + int reverse = EQ (Fcdr (Fassq (Qreverse, f->param_alist)), Qt); + int was_reverse = reverse; + int redraw = 0, fg_set = 0, bg_set = 0; + int need_to_reverse; + unsigned long orig_fg; + unsigned long orig_bg; + Lisp_Object frame_bg, frame_fg; + extern Lisp_Object Qdefault, QCforeground, QCbackground; + + /* If we are creating a new frame, begin with the original screen colors + used for the initial frame. */ + if (alist == Vdefault_frame_alist + && initial_screen_colors[0] != -1 && initial_screen_colors[1] != -1) + { + FRAME_FOREGROUND_PIXEL (f) = initial_screen_colors[0]; + FRAME_BACKGROUND_PIXEL (f) = initial_screen_colors[1]; + } + orig_fg = FRAME_FOREGROUND_PIXEL (f); + orig_bg = FRAME_BACKGROUND_PIXEL (f); + frame_fg = Fcdr (Fassq (Qforeground_color, f->param_alist)); + frame_bg = Fcdr (Fassq (Qbackground_color, f->param_alist)); + /* frame_fg and frame_bg could be nil if, for example, + f->param_alist is nil, e.g. if we are called from + Fmake_terminal_frame. */ + if (NILP (frame_fg)) + frame_fg = build_string (unspecified_fg); + if (NILP (frame_bg)) + frame_bg = build_string (unspecified_bg); + + /* Extract parm names and values into those vectors. */ + i = 0; + for (tail = alist; CONSP (tail); tail = Fcdr (tail)) + { + Lisp_Object elt; + + elt = Fcar (tail); + parms[i] = Fcar (elt); + CHECK_SYMBOL (parms[i], 1); + values[i] = Fcdr (elt); + i++; + } + + j = i; + + for (i = 0; i < j; i++) + { + Lisp_Object prop, val; + + prop = parms[i]; + val = values[i]; + + if (EQ (prop, Qreverse)) + reverse = EQ (val, Qt); + } - redraw = 0; - for (tail = alist; CONSP (tail); tail = Fcdr (tail)) + need_to_reverse = reverse && !was_reverse; + if (termscript && need_to_reverse) + fprintf (termscript, "\n"); + + /* Now process the alist elements in reverse of specified order. */ + for (i--; i >= 0; i--) { - Lisp_Object elt, prop, val; + Lisp_Object prop, val; + Lisp_Object frame; - elt = Fcar (tail); - prop = Fcar (elt); - val = Fcdr (elt); - CHECK_SYMBOL (prop, 1); + prop = parms[i]; + val = values[i]; - if (EQ (prop, intern ("foreground-color"))) + if (EQ (prop, Qforeground_color)) { - unsigned long new_color = load_color (f, val); - if (new_color != ~0) + unsigned long new_color = load_color (f, NULL, val, need_to_reverse + ? LFACE_BACKGROUND_INDEX + : LFACE_FOREGROUND_INDEX); + if (new_color != FACE_TTY_DEFAULT_COLOR + && new_color != FACE_TTY_DEFAULT_FG_COLOR + && new_color != FACE_TTY_DEFAULT_BG_COLOR) { FRAME_FOREGROUND_PIXEL (f) = new_color; + /* Make sure the foreground of the default face for this + frame is changed as well. */ + XSETFRAME (frame, f); + if (need_to_reverse) + { + Finternal_set_lisp_face_attribute (Qdefault, QCbackground, + val, frame); + prop = Qbackground_color; + bg_set = 1; + } + else + { + Finternal_set_lisp_face_attribute (Qdefault, QCforeground, + val, frame); + fg_set = 1; + } redraw = 1; if (termscript) - fprintf (termscript, "\n", new_color); + fprintf (termscript, "\n", new_color); } } - else if (EQ (prop, intern ("background-color"))) + else if (EQ (prop, Qbackground_color)) { - unsigned long new_color = load_color (f, val); - if (new_color != ~0) + unsigned long new_color = load_color (f, NULL, val, need_to_reverse + ? LFACE_FOREGROUND_INDEX + : LFACE_BACKGROUND_INDEX); + if (new_color != FACE_TTY_DEFAULT_COLOR + && new_color != FACE_TTY_DEFAULT_FG_COLOR + && new_color != FACE_TTY_DEFAULT_BG_COLOR) { FRAME_BACKGROUND_PIXEL (f) = new_color; + /* Make sure the background of the default face for this + frame is changed as well. */ + XSETFRAME (frame, f); + if (need_to_reverse) + { + Finternal_set_lisp_face_attribute (Qdefault, QCforeground, + val, frame); + prop = Qforeground_color; + fg_set = 1; + } + else + { + Finternal_set_lisp_face_attribute (Qdefault, QCbackground, + val, frame); + bg_set = 1; + } redraw = 1; if (termscript) - fprintf (termscript, "\n", new_color); + fprintf (termscript, "\n", new_color); } } - else if (EQ (prop, intern ("menu-bar-lines"))) - x_set_menu_bar_lines (f, val, 0); + else if (EQ (prop, Qtitle)) + { + x_set_title (f, val); + if (termscript) + fprintf (termscript, "\n", XSTRING (val)->data); + } + else if (EQ (prop, Qcursor_type)) + { + IT_set_cursor_type (f, val); + if (termscript) + fprintf (termscript, "\n", + EQ (val, Qbar) || CONSP (val) && EQ (XCAR (val), Qbar) + ? "bar" : "box"); + } + store_frame_param (f, prop, val); + } + + /* If they specified "reverse", but not the colors, we need to swap + the current frame colors. */ + if (need_to_reverse) + { + Lisp_Object frame; + + if (!fg_set) + { + XSETFRAME (frame, f); + Finternal_set_lisp_face_attribute (Qdefault, QCforeground, + tty_color_name (f, orig_bg), + frame); + redraw = 1; + } + if (!bg_set) + { + XSETFRAME (frame, f); + Finternal_set_lisp_face_attribute (Qdefault, QCbackground, + tty_color_name (f, orig_fg), + frame); + redraw = 1; + } } if (redraw) { - recompute_basic_faces (f); - if (f == selected_frame) + face_change_count++; /* forces xdisp.c to recompute basic faces */ + if (f == SELECTED_FRAME()) redraw_frame (f); } } +extern void init_frame_faces (FRAME_PTR); + #endif /* !HAVE_X_WINDOWS */ @@ -905,6 +2533,7 @@ internal_terminal_init () { char *term = getenv ("TERM"); char *colors; + struct frame *sf = SELECTED_FRAME(); #ifdef HAVE_X_WINDOWS if (!inhibit_window_system) @@ -920,13 +2549,20 @@ internal_terminal_init () #ifndef HAVE_X_WINDOWS if (!internal_terminal || inhibit_window_system) { - selected_frame->output_method = output_termcap; + sf->output_method = output_termcap; return; } Vwindow_system = intern ("pc"); Vwindow_system_version = make_number (1); - + sf->output_method = output_msdos_raw; + + /* If Emacs was dumped on DOS/V machine, forget the stale VRAM address. */ + screen_old_address = 0; + + /* Forget the stale screen colors as well. */ + initial_screen_colors[0] = initial_screen_colors[1] = -1; + bzero (&the_only_x_display, sizeof the_only_x_display); the_only_x_display.background_pixel = 7; /* White */ the_only_x_display.foreground_pixel = 0; /* Black */ @@ -950,10 +2586,23 @@ internal_terminal_init () } the_only_x_display.line_height = 1; the_only_x_display.font = (XFontStruct *)1; /* must *not* be zero */ - - init_frame_faces (selected_frame); + the_only_x_display.display_info.mouse_face_mouse_frame = NULL; + the_only_x_display.display_info.mouse_face_deferred_gc = 0; + the_only_x_display.display_info.mouse_face_beg_row = + the_only_x_display.display_info.mouse_face_beg_col = -1; + the_only_x_display.display_info.mouse_face_end_row = + the_only_x_display.display_info.mouse_face_end_col = -1; + the_only_x_display.display_info.mouse_face_face_id = DEFAULT_FACE_ID; + the_only_x_display.display_info.mouse_face_window = Qnil; + the_only_x_display.display_info.mouse_face_mouse_x = + the_only_x_display.display_info.mouse_face_mouse_y = 0; + the_only_x_display.display_info.mouse_face_defer = 0; + + init_frame_faces (sf); ring_bell_hook = IT_ring_bell; + insert_glyphs_hook = IT_insert_glyphs; + delete_glyphs_hook = IT_delete_glyphs; write_glyphs_hook = IT_write_glyphs; cursor_to_hook = raw_cursor_to_hook = IT_cursor_to; clear_to_end_hook = IT_clear_to_end; @@ -963,11 +2612,13 @@ internal_terminal_init () update_begin_hook = IT_update_begin; update_end_hook = IT_update_end; reassert_line_highlight_hook = IT_reassert_line_highlight; + frame_up_to_date_hook = IT_frame_up_to_date; /* These hooks are called by term.c without being checked. */ set_terminal_modes_hook = IT_set_terminal_modes; reset_terminal_modes_hook = IT_reset_terminal_modes; set_terminal_window_hook = IT_set_terminal_window; + char_ins_del_ok = 0; #endif } @@ -980,7 +2631,7 @@ dos_get_saved_screen (screen, rows, cols) *screen = startup_screen_buffer; *cols = startup_screen_size_X; *rows = startup_screen_size_Y; - return 1; + return *screen != (char *)0; #else return 0; #endif @@ -992,8 +2643,8 @@ dos_get_saved_screen (screen, rows, cols) void check_x (void) { - if (! FRAME_MSDOS_P (selected_frame)) - error ("Not running under a windows system"); + if (! FRAME_MSDOS_P (SELECTED_FRAME())) + error ("Not running under a window system"); } #endif @@ -1010,13 +2661,33 @@ check_x (void) * SPACE */ +#define Ignore 0x0000 +#define Normal 0x0000 /* normal key - alt changes scan-code */ +#define FctKey 0x1000 /* func key if c == 0, else c */ +#define Special 0x2000 /* func key even if c != 0 */ +#define ModFct 0x3000 /* special if mod-keys, else 'c' */ +#define Map 0x4000 /* alt scan-code, map to unshift/shift key */ +#define KeyPad 0x5000 /* map to insert/kp-0 depending on c == 0xe0 */ +#define Grey 0x6000 /* Grey keypad key */ + +#define Alt 0x0100 /* alt scan-code */ +#define Ctrl 0x0200 /* ctrl scan-code */ +#define Shift 0x0400 /* shift scan-code */ + static int extended_kbd; /* 101 (102) keyboard present. */ +struct kbd_translate { + unsigned char sc; + unsigned char ch; + unsigned short code; +}; + struct dos_keyboard_map { char *unshifted; char *shifted; char *alt_gr; + struct kbd_translate *translate_table; }; @@ -1026,7 +2697,8 @@ static struct dos_keyboard_map us_keyboard = { "`1234567890-= qwertyuiop[] asdfghjkl;'\\ zxcvbnm,./ ", /* 0123456789012345678901234567890123456789 012345678901234 */ "~!@#$%^&*()_+ QWERTYUIOP{} ASDFGHJKL:\"| ZXCVBNM<>? ", - 0 /* no Alt-Gr key */ + 0, /* no Alt-Gr key */ + 0 /* no translate table */ }; static struct dos_keyboard_map fr_keyboard = { @@ -1036,7 +2708,32 @@ static struct dos_keyboard_map fr_keyboard = { /* 0123456789012345678901234567890123456789012345678901234 */ " 1234567890ø+ AZERTYUIOPùœ QSDFGHJKLM%æ WXCVBN?./õ ", /* 01234567 89012345678901234567890123456789012345678901234 */ - " ~#{[|`\\^@]} Ï " + " ~#{[|`\\^@]} Ï ", + 0 /* no translate table */ +}; + +/* + * Italian keyboard support, country code 39. + * '<' 56:3c*0000 + * '>' 56:3e*0000 + * added also {,},` as, respectively, AltGr-8, AltGr-9, AltGr-' + * Donated by Stefano Brozzi + */ + +static struct kbd_translate it_kbd_translate_table[] = { + { 0x56, 0x3c, Normal | 13 }, + { 0x56, 0x3e, Normal | 27 }, + { 0, 0, 0 } +}; +static struct dos_keyboard_map it_keyboard = { +/* 0 1 2 3 4 5 */ +/* 0 123456789012345678901234567890123456789012345678901234 */ + "\\1234567890'< qwertyuiopŠ+> asdfghjkl•…— zxcvbnm,.- ", +/* 01 23456789012345678901234567890123456789012345678901234 */ + "|!\"œ$%&/()=?^> QWERTYUIOP‚* ASDFGHJKL‡øõ ZXCVBNM;:_ ", +/* 0123456789012345678901234567890123456789012345678901234 */ + " {}~` [] @# ", + it_kbd_translate_table }; static struct dos_keyboard_map dk_keyboard = { @@ -1046,7 +2743,27 @@ static struct dos_keyboard_map dk_keyboard = { /* 01 23456789012345678901234567890123456789012345678901234 */ "õ!\"#$%&/()=?` QWERTYUIOP^ ASDFGHJKL’* ZXCVBNM;:_ ", /* 0123456789012345678901234567890123456789012345678901234 */ - " @œ$ {[]} | " + " @œ$ {[]} | ", + 0 /* no translate table */ +}; + +static struct kbd_translate jp_kbd_translate_table[] = { + { 0x73, 0x5c, Normal | 0 }, + { 0x73, 0x5f, Normal | 0 }, + { 0x73, 0x1c, Map | 0 }, + { 0x7d, 0x5c, Normal | 13 }, + { 0x7d, 0x7c, Normal | 13 }, + { 0x7d, 0x1c, Map | 13 }, + { 0, 0, 0 } +}; +static struct dos_keyboard_map jp_keyboard = { +/* 0 1 2 3 4 5 */ +/* 0123456789012 345678901234567890123456789012345678901234 */ + "\\1234567890-^\\ qwertyuiop@[ asdfghjkl;:] zxcvbnm,./ ", +/* 01 23456789012345678901234567890123456789012345678901234 */ + "_!\"#$%&'()~=~| QWERTYUIOP`{ ASDFGHJKL+*} ZXCVBNM<>? ", + 0, /* no Alt-Gr key */ + jp_kbd_translate_table }; static struct keyboard_layout_list @@ -1057,11 +2774,14 @@ static struct keyboard_layout_list { 1, &us_keyboard, 33, &fr_keyboard, - 45, &dk_keyboard + 39, &it_keyboard, + 45, &dk_keyboard, + 81, &jp_keyboard }; static struct dos_keyboard_map *keyboard; static int keyboard_map_all; +static int international_keyboard; int dos_set_keyboard (code, always) @@ -1069,6 +2789,16 @@ dos_set_keyboard (code, always) int always; { int i; + _go32_dpmi_registers regs; + + /* See if Keyb.Com is installed (for international keyboard support). + Note: calling Int 2Fh via int86 wedges the DOS box on some versions + of Windows 9X! So don't do that! */ + regs.x.ax = 0xad80; + regs.x.ss = regs.x.sp = regs.x.flags = 0; + _go32_dpmi_simulate_int (0x2f, ®s); + if (regs.h.al == 0xff) + international_keyboard = 1; /* Initialize to US settings, for countries that don't have their own. */ keyboard = keyboard_layout_list[0].keyboard_map; @@ -1086,19 +2816,6 @@ dos_set_keyboard (code, always) return 0; } -#define Ignore 0x0000 -#define Normal 0x0000 /* normal key - alt changes scan-code */ -#define FctKey 0x1000 /* func key if c == 0, else c */ -#define Special 0x2000 /* func key even if c != 0 */ -#define ModFct 0x3000 /* special if mod-keys, else 'c' */ -#define Map 0x4000 /* alt scan-code, map to unshift/shift key */ -#define KeyPad 0x5000 /* map to insert/kp-0 depending on c == 0xe0 */ -#define Grey 0x6000 /* Grey keypad key */ - -#define Alt 0x0100 /* alt scan-code */ -#define Ctrl 0x0200 /* ctrl scan-code */ -#define Shift 0x0400 /* shift scan-code */ - static struct { unsigned char char_code; /* normal code */ @@ -1198,7 +2915,7 @@ ibmpc_translate_map[] = Ignore, /* Right shift */ Grey | 1, /* Grey * */ Ignore, /* Alt */ - Normal | ' ', /* ' ' */ + Normal | 55, /* ' ' */ Ignore, /* Caps Lock */ FctKey | 0xbe, /* F1 */ FctKey | 0xbf, /* F2 */ @@ -1376,6 +3093,13 @@ dos_get_modifiers (keymask) mask |= SUPER_P; modifiers |= super_modifier; } + else if (!international_keyboard) + { + /* If Keyb.Com is NOT installed, let Right Alt behave + like the Left Alt. */ + mask &= ~ALT_GR_P; + mask |= ALT_P; + } } if (regs.h.ah & 1) /* Left CTRL pressed ? */ @@ -1440,7 +3164,6 @@ and then the scan code.") } /* Get a char from keyboard. Function keys are put into the event queue. */ - static int dos_rawgetc () { @@ -1448,10 +3171,10 @@ dos_rawgetc () union REGS regs; #ifndef HAVE_X_WINDOWS - SCREEN_SET_CURSOR (); - if (!mouse_visible) mouse_on (); + /* Maybe put the cursor where it should be. */ + IT_cmgoto (SELECTED_FRAME()); #endif - + /* The following condition is equivalent to `kbhit ()', except that it uses the bios to do its job. This pleases DESQview/X. */ while ((regs.h.ah = extended_kbd ? 0x11 : 0x01), @@ -1460,7 +3183,7 @@ dos_rawgetc () { union REGS regs; register unsigned char c; - int sc, code, mask, kp_mode; + int sc, code = -1, mask, kp_mode; int modifiers; regs.h.ah = extended_kbd ? 0x10 : 0x00; @@ -1510,15 +3233,47 @@ dos_rawgetc () } else { - if (sc >= (sizeof (ibmpc_translate_map) / sizeof (short))) - continue; - if ((code = ibmpc_translate_map[sc]) == Ignore) - continue; + /* Try the keyboard-private translation table first. */ + if (keyboard->translate_table) + { + struct kbd_translate *p = keyboard->translate_table; + + while (p->sc) + { + if (p->sc == sc && p->ch == c) + { + code = p->code; + break; + } + p++; + } + } + /* If the private table didn't translate it, use the general + one. */ + if (code == -1) + { + if (sc >= (sizeof (ibmpc_translate_map) / sizeof (short))) + continue; + if ((code = ibmpc_translate_map[sc]) == Ignore) + continue; + } } if (c == 0) { - if (code & Alt) + /* We only look at the keyboard Ctrl/Shift/Alt keys when + Emacs is ready to read a key. Therefore, if they press + `Alt-x' when Emacs is busy, by the time we get to + `dos_get_modifiers', they might have already released the + Alt key, and Emacs gets just `x', which is BAD. + However, for keys with the `Map' property set, the ASCII + code returns zero iff Alt is pressed. So, when we DON'T + have to support international_keyboard, we don't have to + distinguish between the left and right Alt keys, and we + can set the META modifier for any keys with the `Map' + property if they return zero ASCII code (c = 0). */ + if ( (code & Alt) + || ( (code & 0xf000) == Map && !international_keyboard)) modifiers |= meta_modifier; if (code & Ctrl) modifiers |= ctrl_modifier; @@ -1630,18 +3385,49 @@ dos_rawgetc () event.kind = ascii_keystroke; event.code = code; event.modifiers = modifiers; - XSETFRAME (event.frame_or_window, selected_frame); + event.frame_or_window = selected_frame; + event.arg = Qnil; event.timestamp = event_timestamp (); kbd_buffer_store_event (&event); } - if (have_mouse > 0) + if (have_mouse > 0 && !mouse_preempted) { int but, press, x, y, ok; + int mouse_prev_x = mouse_last_x, mouse_prev_y = mouse_last_y; /* Check for mouse movement *before* buttons. */ mouse_check_moved (); + /* If the mouse moved from the spot of its last sighting, we + might need to update mouse highlight. */ + if (mouse_last_x != mouse_prev_x || mouse_last_y != mouse_prev_y) + { + previous_help_echo = help_echo; + help_echo = help_echo_object = help_echo_window = Qnil; + help_echo_pos = -1; + IT_note_mouse_highlight (SELECTED_FRAME(), + mouse_last_x, mouse_last_y); + /* If the contents of the global variable help_echo has + changed, generate a HELP_EVENT. */ + if (!NILP (help_echo) || !NILP (previous_help_echo)) + { + /* HELP_EVENT takes 2 events in the event loop. */ + event.kind = HELP_EVENT; + event.frame_or_window = selected_frame; + event.arg = help_echo_object; + event.x = make_number (help_echo_pos); + event.timestamp = event_timestamp (); + event.code = 0; + kbd_buffer_store_event (&event); + if (WINDOWP (help_echo_window)) + event.frame_or_window = help_echo_window; + event.arg = help_echo; + event.code = 1; + kbd_buffer_store_event (&event); + } + } + for (but = 0; but < NUM_MOUSE_BUTTONS; but++) for (press = 0; press < 2; press++) { @@ -1680,7 +3466,8 @@ dos_rawgetc () | (press ? down_modifier : up_modifier); event.x = x; event.y = y; - XSETFRAME (event.frame_or_window, selected_frame); + event.frame_or_window = selected_frame; + event.arg = Qnil; event.timestamp = event_timestamp (); kbd_buffer_store_event (&event); } @@ -1723,7 +3510,7 @@ pixel_to_glyph_coords (f, pix_x, pix_y, x, y, bounds, noclip) FRAME_PTR f; register int pix_x, pix_y; register int *x, *y; - void /* XRectangle */ *bounds; + XRectangle *bounds; int noclip; { if (bounds) abort (); @@ -1752,6 +3539,12 @@ glyph_to_pixel_coords (f, x, y, pix_x, pix_y) grab the nearest Xlib manual (down the hall, second-to-last door on the left), but I don't think it's worth the effort. */ +/* These hold text of the current and the previous menu help messages. */ +static char *menu_help_message, *prev_menu_help_message; +/* Pane number and item number of the menu item which generated the + last menu help message. */ +static int menu_help_paneno, menu_help_itemno; + static XMenu * IT_menu_create () { @@ -1774,6 +3567,7 @@ IT_menu_make_room (XMenu *menu) menu->text = (char **) xmalloc (count * sizeof (char *)); menu->submenu = (XMenu **) xmalloc (count * sizeof (XMenu *)); menu->panenumber = (int *) xmalloc (count * sizeof (int)); + menu->help_text = (char **) xmalloc (count * sizeof (char *)); } else if (menu->allocated == menu->count) { @@ -1784,6 +3578,8 @@ IT_menu_make_room (XMenu *menu) = (XMenu **) xrealloc (menu->submenu, count * sizeof (XMenu *)); menu->panenumber = (int *) xrealloc (menu->panenumber, count * sizeof (int)); + menu->help_text + = (char **) xrealloc (menu->help_text, count * sizeof (char *)); } } @@ -1831,47 +3627,73 @@ IT_menu_calc_size (XMenu *menu, int *width, int *height) /* Display MENU at (X,Y) using FACES. */ static void -IT_menu_display (XMenu *menu, int y, int x, int *faces) +IT_menu_display (XMenu *menu, int y, int x, int pn, int *faces, int disp_help) { int i, j, face, width; - GLYPH *text, *p; + struct glyph *text, *p; char *q; int mx, my; int enabled, mousehere; int row, col; + struct frame *sf = SELECTED_FRAME(); + + menu_help_message = NULL; width = menu->width; - text = (GLYPH *) xmalloc ((width + 2) * sizeof (GLYPH)); + text = (struct glyph *) xmalloc ((width + 2) * sizeof (struct glyph)); ScreenGetCursor (&row, &col); mouse_get_xy (&mx, &my); - IT_update_begin (); + IT_update_begin (sf); for (i = 0; i < menu->count; i++) { + int max_width = width + 2; + IT_cursor_to (y + i, x); enabled = (!menu->submenu[i] && menu->panenumber[i]) || (menu->submenu[i]); mousehere = (y + i == my && x <= mx && mx < x + width + 2); face = faces[enabled + mousehere * 2]; + /* The following if clause means that we display the menu help + strings even if the menu item is currently disabled. */ + if (disp_help && enabled + mousehere * 2 >= 2) + { + menu_help_message = menu->help_text[i]; + menu_help_paneno = pn - 1; + menu_help_itemno = i; + } p = text; - *p++ = FAST_MAKE_GLYPH (' ', face); + SET_CHAR_GLYPH (*p, ' ', face, 0); + p++; for (j = 0, q = menu->text[i]; *q; j++) { if (*q > 26) - *p++ = FAST_MAKE_GLYPH (*q++, face); + { + SET_CHAR_GLYPH (*p, *q++, face, 0); + p++; + } else /* make '^x' */ { - *p++ = FAST_MAKE_GLYPH ('^', face); + SET_CHAR_GLYPH (*p, '^', face, 0); + p++; j++; - *p++ = FAST_MAKE_GLYPH (*q++ + 64, face); + SET_CHAR_GLYPH (*p, *q++ + 64, face, 0); + p++; } } - - for (; j < width; j++) - *p++ = FAST_MAKE_GLYPH (' ', face); - *p++ = FAST_MAKE_GLYPH (menu->submenu[i] ? 16 : ' ', face); - IT_write_glyphs (text, width + 2); + /* Don't let the menu text overflow into the next screen row. */ + if (x + max_width > screen_size_X) + { + max_width = screen_size_X - x; + text[max_width - 1].u.ch = '$'; /* indicate it's truncated */ + } + for (; j < max_width - 2; j++, p++) + SET_CHAR_GLYPH (*p, ' ', face, 0); + + SET_CHAR_GLYPH (*p, menu->submenu[i] ? 16 : ' ', face, 0); + p++; + IT_write_glyphs (text, max_width); } - IT_update_end (); + IT_update_end (sf); IT_cursor_to (row, col); xfree (text); } @@ -1911,6 +3733,7 @@ XMenuAddPane (Display *foo, XMenu *menu, char *txt, int enable) menu->submenu[menu->count] = IT_menu_create (); menu->text[menu->count] = txt; menu->panenumber[menu->count] = ++menu->panecount; + menu->help_text[menu->count] = NULL; menu->count++; /* Adjust length for possible control characters (which will @@ -1929,7 +3752,7 @@ XMenuAddPane (Display *foo, XMenu *menu, char *txt, int enable) int XMenuAddSelection (Display *bar, XMenu *menu, int pane, - int foo, char *txt, int enable) + int foo, char *txt, int enable, char *help_text) { int len; char *p; @@ -1941,6 +3764,7 @@ XMenuAddSelection (Display *bar, XMenu *menu, int pane, menu->submenu[menu->count] = (XMenu *) 0; menu->text[menu->count] = txt; menu->panenumber[menu->count] = enable; + menu->help_text[menu->count] = help_text; menu->count++; /* Adjust length for possible control characters (which will @@ -1980,39 +3804,48 @@ struct IT_menu_state int XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx, - int x0, int y0, unsigned ButtonMask, char **txt) + int x0, int y0, unsigned ButtonMask, char **txt, + void (*help_callback)(char *, int, int)) { struct IT_menu_state *state; int statecount; int x, y, i, b; int screensize; - int faces[4], selectface; + int faces[4]; + Lisp_Object selectface; int leave, result, onepane; int title_faces[4]; /* face to display the menu title */ int buffers_num_deleted = 0; + struct frame *sf = SELECTED_FRAME(); + Lisp_Object saved_echo_area_message; /* Just in case we got here without a mouse present... */ if (have_mouse <= 0) return XM_IA_SELECT; + /* Don't allow non-positive x0 and y0, lest the menu will wrap + around the display. */ + if (x0 <= 0) + x0 = 1; + if (y0 <= 0) + y0 = 1; + + /* We will process all the mouse events directly, so we had + better prevent dos_rawgetc from stealing them from us. */ + mouse_preempted++; state = alloca (menu->panecount * sizeof (struct IT_menu_state)); screensize = screen_size * 2; faces[0] - = compute_glyph_face (selected_frame, - face_name_id_number - (selected_frame, - intern ("msdos-menu-passive-face")), - 0); + = lookup_derived_face (sf, intern ("msdos-menu-passive-face"), + 0, DEFAULT_FACE_ID); faces[1] - = compute_glyph_face (selected_frame, - face_name_id_number - (selected_frame, - intern ("msdos-menu-active-face")), - 0); - selectface - = face_name_id_number (selected_frame, intern ("msdos-menu-select-face")); - faces[2] = compute_glyph_face (selected_frame, selectface, faces[0]); - faces[3] = compute_glyph_face (selected_frame, selectface, faces[1]); + = lookup_derived_face (sf, intern ("msdos-menu-active-face"), + 0, DEFAULT_FACE_ID); + selectface = intern ("msdos-menu-select-face"); + faces[2] = lookup_derived_face (sf, selectface, + 0, faces[0]); + faces[3] = lookup_derived_face (sf, selectface, + 0, faces[1]); /* Make sure the menu title is always displayed with `msdos-menu-active-face', no matter where the mouse pointer is. */ @@ -2032,11 +3865,21 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx, menu->text[0][7] = '\0'; buffers_num_deleted = 1; } + + /* We need to save the current echo area message, so that we could + restore it below, before we exit. See the commentary below, + before the call to message_with_string. */ + saved_echo_area_message = Fcurrent_message (); state[0].menu = menu; mouse_off (); ScreenRetrieve (state[0].screen_behind = xmalloc (screensize)); - IT_menu_display (menu, y0 - 1, x0 - 1, title_faces); /* display menu title */ + /* Turn off the cursor. Otherwise it shows through the menu + panes, which is ugly. */ + IT_display_cursor (0); + + /* Display the menu title. */ + IT_menu_display (menu, y0 - 1, x0 - 1, 1, title_faces, 0); if (buffers_num_deleted) menu->text[0][7] = ' '; if ((onepane = menu->count == 1 && menu->submenu[0])) @@ -2058,9 +3901,9 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx, { if (!mouse_visible) mouse_on (); mouse_check_moved (); - if (selected_frame->mouse_moved) + if (sf->mouse_moved) { - selected_frame->mouse_moved = 0; + sf->mouse_moved = 0; result = XM_IA_SELECT; mouse_get_xy (&x, &y); for (i = 0; i < statecount; i++) @@ -2086,6 +3929,8 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx, statecount--; mouse_off (); ScreenUpdate (state[statecount].screen_behind); + if (screen_virtual_segment) + dosv_refresh_virtual_screen (0, screen_size); xfree (state[statecount].screen_behind); } if (i == statecount - 1 && state[i].menu->submenu[dy]) @@ -2093,7 +3938,8 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx, IT_menu_display (state[i].menu, state[i].y, state[i].x, - faces); + state[i].pane, + faces, 1); state[statecount].menu = state[i].menu->submenu[dy]; state[statecount].pane = state[i].menu->panenumber[dy]; mouse_off (); @@ -2109,20 +3955,77 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx, IT_menu_display (state[statecount - 1].menu, state[statecount - 1].y, state[statecount - 1].x, - faces); + state[statecount - 1].pane, + faces, 1); + } + else + { + if ((menu_help_message || prev_menu_help_message) + && menu_help_message != prev_menu_help_message) + { + help_callback (menu_help_message, + menu_help_paneno, menu_help_itemno); + IT_display_cursor (0); + prev_menu_help_message = menu_help_message; + } + /* We are busy-waiting for the mouse to move, so let's be nice + to other Windows applications by releasing our time slice. */ + __dpmi_yield (); } - for (b = 0; b < mouse_button_count; b++) + for (b = 0; b < mouse_button_count && !leave; b++) { - (void) mouse_pressed (b, &x, &y); - if (mouse_released (b, &x, &y)) - leave = 1; + /* Only leave if user both pressed and released the mouse, and in + that order. This avoids popping down the menu pane unless + the user is really done with it. */ + if (mouse_pressed (b, &x, &y)) + { + while (mouse_button_depressed (b, &x, &y)) + __dpmi_yield (); + leave = 1; + } + (void) mouse_released (b, &x, &y); } } mouse_off (); ScreenUpdate (state[0].screen_behind); + if (screen_virtual_segment) + dosv_refresh_virtual_screen (0, screen_size); + + /* We have a situation here. ScreenUpdate has just restored the + screen contents as it was before we started drawing this menu. + That includes any echo area message that could have been + displayed back then. (In reality, that echo area message will + almost always be the ``keystroke echo'' that echoes the sequence + of menu items chosen by the user.) However, if the menu had some + help messages, then displaying those messages caused Emacs to + forget about the original echo area message. So when + ScreenUpdate restored it, it created a discrepancy between the + actual screen contents and what Emacs internal data structures + know about it. + + To avoid this conflict, we force Emacs to restore the original + echo area message as we found it when we entered this function. + The irony of this is that we then erase the restored message + right away, so the only purpose of restoring it is so that + erasing it works correctly... */ + if (! NILP (saved_echo_area_message)) + message_with_string ("%s", saved_echo_area_message, 0); + message (0); while (statecount--) xfree (state[statecount].screen_behind); + IT_display_cursor (1); /* turn cursor back on */ + /* Clean up any mouse events that are waiting inside Emacs event queue. + These events are likely to be generated before the menu was even + displayed, probably because the user pressed and released the button + (which invoked the menu) too quickly. If we don't remove these events, + Emacs will process them after we return and surprise the user. */ + discard_mouse_events (); + mouse_clear_clicks (); + if (!kbd_buffer_events_waiting (1)) + clear_input_pending (); + /* Allow mouse events generation by dos_rawgetc. */ + mouse_preempted--; return result; } @@ -2140,8 +4043,10 @@ XMenuDestroy (Display *foo, XMenu *menu) xfree (menu->text); xfree (menu->submenu); xfree (menu->panenumber); + xfree (menu->help_text); } xfree (menu); + menu_help_message = prev_menu_help_message = NULL; } int @@ -2218,7 +4123,9 @@ getdefdir (drive, dst) *p = '\0'; errno = 0; _fixpath (in_path, dst); - if (errno) + /* _fixpath can set errno to ENOSYS on non-LFN systems because + it queries the LFN support, so ignore that error. */ + if ((errno && errno != ENOSYS) || *dst == '\0') return 0; msdos_downcase_filename (dst); @@ -2237,7 +4144,6 @@ crlf_to_lf (n, buf) unsigned char *np = buf; unsigned char *startp = buf; unsigned char *endp = buf + n; - unsigned char c; if (n == 0) return n; @@ -2327,6 +4233,122 @@ __write (int handle, const void *buffer, size_t count) } } +/* A low-level file-renaming function which works around Windows 95 bug. + This is pulled directly out of DJGPP v2.01 library sources, and only + used when you compile with DJGPP v2.0. */ + +#include + +int _rename(const char *old, const char *new) +{ + __dpmi_regs r; + int olen = strlen(old) + 1; + int i; + int use_lfn = _USE_LFN; + char tempfile[FILENAME_MAX]; + const char *orig = old; + int lfn_fd = -1; + + r.x.dx = __tb_offset; + r.x.di = __tb_offset + olen; + r.x.ds = r.x.es = __tb_segment; + + if (use_lfn) + { + /* Windows 95 bug: for some filenames, when you rename + file -> file~ (as in Emacs, to leave a backup), the + short 8+3 alias doesn't change, which effectively + makes OLD and NEW the same file. We must rename + through a temporary file to work around this. */ + + char *pbase = 0, *p; + static char try_char[] = "abcdefghijklmnopqrstuvwxyz012345789"; + int idx = sizeof(try_char) - 1; + + /* Generate a temporary name. Can't use `tmpnam', since $TMPDIR + might point to another drive, which will fail the DOS call. */ + strcpy(tempfile, old); + for (p = tempfile; *p; p++) /* ensure temporary is on the same drive */ + if (*p == '/' || *p == '\\' || *p == ':') + pbase = p; + if (pbase) + pbase++; + else + pbase = tempfile; + strcpy(pbase, "X$$djren$$.$$temp$$"); + + do + { + if (idx <= 0) + return -1; + *pbase = try_char[--idx]; + } while (_chmod(tempfile, 0) != -1); + + r.x.ax = 0x7156; + _put_path2(tempfile, olen); + _put_path(old); + __dpmi_int(0x21, &r); + if (r.x.flags & 1) + { + errno = __doserr_to_errno(r.x.ax); + return -1; + } + + /* Now create a file with the original name. This will + ensure that NEW will always have a 8+3 alias + different from that of OLD. (Seems to be required + when NameNumericTail in the Registry is set to 0.) */ + lfn_fd = _creat(old, 0); + + olen = strlen(tempfile) + 1; + old = tempfile; + r.x.di = __tb_offset + olen; + } + + for (i=0; i<2; i++) + { + if(use_lfn) + r.x.ax = 0x7156; + else + r.h.ah = 0x56; + _put_path2(new, olen); + _put_path(old); + __dpmi_int(0x21, &r); + if(r.x.flags & 1) + { + if (r.x.ax == 5 && i == 0) /* access denied */ + remove(new); /* and try again */ + else + { + errno = __doserr_to_errno(r.x.ax); + + /* Restore to original name if we renamed it to temporary. */ + if (use_lfn) + { + if (lfn_fd != -1) + { + _close (lfn_fd); + remove (orig); + } + _put_path2(orig, olen); + _put_path(tempfile); + r.x.ax = 0x7156; + __dpmi_int(0x21, &r); + } + return -1; + } + } + else + break; + } + + /* Success. Delete the file possibly created to work + around the Windows 95 bug. */ + if (lfn_fd != -1) + return (_close (lfn_fd) == 0) ? remove (orig) : -1; + return 0; +} + #endif /* __DJGPP__ == 2 && __DJGPP_MINOR__ == 0 */ DEFUN ("msdos-long-file-names", Fmsdos_long_file_names, Smsdos_long_file_names, @@ -2370,7 +4392,6 @@ The argument object is never altered--the value is a copy.") (filename) Lisp_Object filename; { - char *fname; Lisp_Object tem; if (! STRINGP (filename)) @@ -2409,6 +4430,44 @@ init_environment (argc, argv, skip_args) { char *s, *t, *root; int len; + static const char * const tempdirs[] = { + "$TMPDIR", "$TEMP", "$TMP", "c:/" + }; + int i; + const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]); + + /* Make sure they have a usable $TMPDIR. Many Emacs functions use + temporary files and assume "/tmp" if $TMPDIR is unset, which + will break on DOS/Windows. Refuse to work if we cannot find + a directory, not even "c:/", usable for that purpose. */ + for (i = 0; i < imax ; i++) + { + const char *tmp = tempdirs[i]; + + if (*tmp == '$') + tmp = getenv (tmp + 1); + /* Note that `access' can lie to us if the directory resides on a + read-only filesystem, like CD-ROM or a write-protected floppy. + The only way to be really sure is to actually create a file and + see if it succeeds. But I think that's too much to ask. */ + if (tmp && access (tmp, D_OK) == 0) + { + setenv ("TMPDIR", tmp, 1); + break; + } + } + if (i >= imax) + cmd_error_internal + (Fcons (Qerror, + Fcons (build_string ("no usable temporary directories found!!"), + Qnil)), + "While setting TMPDIR: "); + + /* Note the startup time, so we know not to clear the screen if we + exit immediately; see IT_reset_terminal_modes. + (Yes, I know `clock' returns zero the first time it's called, but + I do this anyway, in case some wiseguy changes that at some point.) */ + startup_time = clock (); /* Find our root from argv[0]. Assuming argv[0] is, say, "c:/emacs/bin/emacs.exe" our root will be "c:/emacs". */ @@ -2419,10 +4478,12 @@ init_environment (argc, argv, skip_args) while (len > 0 && root[len] != '/' && root[len] != ':') len--; root[len] = '\0'; - if (len > 4 && strcmp (root + len - 4, "/bin") == 0) + if (len > 4 + && (strcmp (root + len - 4, "/bin") == 0 + || strcmp (root + len - 4, "/src") == 0)) /* under a debugger */ root[len - 4] = '\0'; else - strcpy (root, "c:/emacs"); /* Only under debuggers, I think. */ + strcpy (root, "c:/emacs"); /* let's be defensive */ len = strlen (root); strcpy (emacsroot, root); @@ -2596,23 +4657,17 @@ dos_ttraw () { have_mouse = 1; /* enable mouse */ mouse_visible = 0; - - if (outregs.x.bx == 3) - { - mouse_button_count = 3; - mouse_button_translate[0] = 0; /* Left */ - mouse_button_translate[1] = 2; /* Middle */ - mouse_button_translate[2] = 1; /* Right */ - } - else - { - mouse_button_count = 2; - mouse_button_translate[0] = 0; - mouse_button_translate[1] = 1; - } + mouse_setup_buttons (outregs.x.bx); mouse_position_hook = &mouse_get_pos; mouse_init (); } + +#ifndef HAVE_X_WINDOWS +#if __DJGPP__ >= 2 + /* Save the cursor shape used outside Emacs. */ + outside_cursor = _farpeekw (_dos_ds, 0x460); +#endif +#endif } first_time = 0; @@ -2658,6 +4713,16 @@ dos_ttcooked () #if __DJGPP__ >= 2 +#ifndef HAVE_X_WINDOWS + /* Restore the cursor shape we found on startup. */ + if (outside_cursor) + { + inregs.h.ah = 1; + inregs.x.cx = outside_cursor; + int86 (0x10, &inregs, &outregs); + } +#endif + return (setmode (fileno (stdin), stdin_stat) != -1); #else /* not __DJGPP__ >= 2 */ @@ -2677,15 +4742,16 @@ dos_ttcooked () file TEMPOUT and stderr to TEMPERR. */ int -run_msdos_command (argv, dir, tempin, tempout, temperr) +run_msdos_command (argv, working_dir, tempin, tempout, temperr, envv) unsigned char **argv; - Lisp_Object dir; + const char *working_dir; int tempin, tempout, temperr; + char **envv; { - char *saveargv1, *saveargv2, **envv, *lowcase_argv0, *pa, *pl; + char *saveargv1, *saveargv2, *lowcase_argv0, *pa, *pl; char oldwd[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS. */ int msshell, result = -1; - int in, out, inbak, outbak, errbak; + int inbak, outbak, errbak; int x, y; Lisp_Object cmd; @@ -2712,44 +4778,27 @@ run_msdos_command (argv, dir, tempin, tempout, temperr) saveargv1 = argv[1]; saveargv2 = argv[2]; argv[1] = "/c"; - if (argv[2]) + /* We only need to mirror slashes if a DOS shell will be invoked + not via `system' (which does the mirroring itself). Yes, that + means DJGPP v1.x will lose here. */ + if (argv[2] && argv[3]) { char *p = alloca (strlen (argv[2]) + 1); strcpy (argv[2] = p, saveargv2); while (*p && isspace (*p)) p++; - while (*p && !isspace (*p)) - if (*p == '/') - *p++ = '\\'; - else - p++; + while (*p) + { + if (*p == '/') + *p++ = '\\'; + else + p++; + } } } - /* Build the environment array. */ - { - extern Lisp_Object Vprocess_environment; - Lisp_Object tmp, lst; - int i, len; - - lst = Vprocess_environment; - len = XFASTINT (Flength (lst)); - - envv = alloca ((len + 1) * sizeof (char *)); - for (i = 0; i < len; i++) - { - tmp = Fcar (lst); - lst = Fcdr (lst); - CHECK_STRING (tmp, 0); - envv[i] = alloca (XSTRING (tmp)->size + 1); - strcpy (envv[i], XSTRING (tmp)->data); - } - envv[len] = (char *) 0; - } - - if (STRINGP (dir)) - chdir (XSTRING (dir)->data); + chdir (working_dir); inbak = dup (0); outbak = dup (1); errbak = dup (2); @@ -2773,12 +4822,38 @@ run_msdos_command (argv, dir, tempin, tempout, temperr) cannot grok commands longer than 126 characters. In DJGPP v2 and later, `system' is much smarter, so we'll call it instead. */ - extern char **environ; - environ = envv; + const char *cmnd; /* A shell gets a single argument--its full command line--whose original was saved in `saveargv2'. */ - result = system (saveargv2); + + /* Don't let them pass empty command lines to `system', since + with some shells it will try to invoke an interactive shell, + which will hang Emacs. */ + for (cmnd = saveargv2; *cmnd && isspace (*cmnd); cmnd++) + ; + if (*cmnd) + { + extern char **environ; + char **save_env = environ; + int save_system_flags = __system_flags; + + /* Request the most powerful version of `system'. We need + all the help we can get to avoid calling stock DOS shells. */ + __system_flags = (__system_redirect + | __system_use_shell + | __system_allow_multiple_cmds + | __system_allow_long_cmds + | __system_handle_null_commands + | __system_emulate_chdir); + + environ = envv; + result = system (cmnd); + __system_flags = save_system_flags; + environ = save_env; + } + else + result = 0; /* emulate Unixy shell behavior with empty cmd line */ } else @@ -2789,9 +4864,9 @@ run_msdos_command (argv, dir, tempin, tempout, temperr) dup2 (inbak, 0); dup2 (outbak, 1); dup2 (errbak, 2); - close (inbak); - close (outbak); - close (errbak); + emacs_close (inbak); + emacs_close (outbak); + emacs_close (errbak); dos_ttraw (); if (have_mouse > 0) @@ -2799,6 +4874,11 @@ run_msdos_command (argv, dir, tempin, tempout, temperr) mouse_init (); mouse_moveto (x, y); } + + /* Some programs might change the meaning of the highest bit of the + text attribute byte, so we get blinking characters instead of the + bright background colors. Restore that. */ + bright_bg (); done: chdir (oldwd); @@ -2892,13 +4972,122 @@ int kill (x, y) int x, y; { return -1; } nice (p) int p; {} void volatile pause () {} sigsetmask (x) int x; { return 0; } +sigblock (mask) int mask; { return 0; } #endif -request_sigio () {} +void request_sigio (void) {} setpgrp () {return 0; } setpriority (x,y,z) int x,y,z; { return 0; } +void unrequest_sigio (void) {} + +#if __DJGPP__ > 1 + +#ifdef POSIX_SIGNALS + +/* Augment DJGPP library POSIX signal functions. This is needed + as of DJGPP v2.01, but might be in the library in later releases. */ + +#include + +/* A counter to know when to re-initialize the static sets. */ +static int sigprocmask_count = -1; + +/* Which signals are currently blocked (initially none). */ +static sigset_t current_mask; + +/* Which signals are pending (initially none). */ +static sigset_t pending_signals; + +/* Previous handlers to restore when the blocked signals are unblocked. */ +typedef void (*sighandler_t)(int); +static sighandler_t prev_handlers[320]; + +/* A signal handler which just records that a signal occured + (it will be raised later, if and when the signal is unblocked). */ +static void +sig_suspender (signo) + int signo; +{ + sigaddset (&pending_signals, signo); +} + +int +sigprocmask (how, new_set, old_set) + int how; + const sigset_t *new_set; + sigset_t *old_set; +{ + int signo; + sigset_t new_mask; + + /* If called for the first time, initialize. */ + if (sigprocmask_count != __bss_count) + { + sigprocmask_count = __bss_count; + sigemptyset (&pending_signals); + sigemptyset (¤t_mask); + for (signo = 0; signo < 320; signo++) + prev_handlers[signo] = SIG_ERR; + } + + if (old_set) + *old_set = current_mask; + + if (new_set == 0) + return 0; + + if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK) + { + errno = EINVAL; + return -1; + } + + sigemptyset (&new_mask); + + /* DJGPP supports upto 320 signals. */ + for (signo = 0; signo < 320; signo++) + { + if (sigismember (¤t_mask, signo)) + sigaddset (&new_mask, signo); + else if (sigismember (new_set, signo) && how != SIG_UNBLOCK) + { + sigaddset (&new_mask, signo); + + /* SIGKILL is silently ignored, as on other platforms. */ + if (signo != SIGKILL && prev_handlers[signo] == SIG_ERR) + prev_handlers[signo] = signal (signo, sig_suspender); + } + if (( how == SIG_UNBLOCK + && sigismember (&new_mask, signo) + && sigismember (new_set, signo)) + || (how == SIG_SETMASK + && sigismember (&new_mask, signo) + && !sigismember (new_set, signo))) + { + sigdelset (&new_mask, signo); + if (prev_handlers[signo] != SIG_ERR) + { + signal (signo, prev_handlers[signo]); + prev_handlers[signo] = SIG_ERR; + } + if (sigismember (&pending_signals, signo)) + { + sigdelset (&pending_signals, signo); + raise (signo); + } + } + } + current_mask = new_mask; + return 0; +} + +#else /* not POSIX_SIGNALS */ + +sigsetmask (x) int x; { return 0; } sigblock (mask) int mask; { return 0; } -unrequest_sigio () {} + +#endif /* not POSIX_SIGNALS */ +#endif /* __DJGPP__ > 1 */ #ifndef HAVE_SELECT #include "sysselect.h" @@ -2910,6 +5099,25 @@ unrequest_sigio () {} && (long)(time).tv_usec <= 0)) #endif +/* This yields the rest of the current time slice to the task manager. + It should be called by any code which knows that it has nothing + useful to do except idle. + + I don't use __dpmi_yield here, since versions of library before 2.02 + called Int 2Fh/AX=1680h there in a way that would wedge the DOS box + on some versions of Windows 9X. */ + +void +dos_yield_time_slice (void) +{ + _go32_dpmi_registers r; + + r.x.ax = 0x1680; + r.x.ss = r.x.sp = r.x.flags = 0; + _go32_dpmi_simulate_int (0x2f, &r); + if (r.h.al == 0x80) + errno = ENOSYS; +} /* Only event queue is checked. */ /* We don't have to call timer_check here @@ -2943,9 +5151,7 @@ sys_select (nfds, rfds, wfds, efds, timeout) { while (!detect_input_pending ()) { -#if __DJGPP__ >= 2 - __dpmi_yield (); -#endif + dos_yield_time_slice (); } } else @@ -2971,9 +5177,7 @@ sys_select (nfds, rfds, wfds, efds, timeout) if (EMACS_TIME_ZERO_OR_NEG_P (*timeout)) return 0; cllast = clnow; -#if __DJGPP__ >= 2 - __dpmi_yield (); -#endif + dos_yield_time_slice (); } } @@ -3067,22 +5271,84 @@ abort () ScreenSetCursor (10, 0); cputs ("\r\n\nEmacs aborted!\r\n"); #if __DJGPP__ > 1 +#if __DJGPP__ == 2 && __DJGPP_MINOR__ < 2 + if (screen_virtual_segment) + dosv_refresh_virtual_screen (2 * 10 * screen_size_X, 4 * screen_size_X); /* Generate traceback, so we could tell whodunit. */ signal (SIGINT, SIG_DFL); __asm__ __volatile__ ("movb $0x1b,%al;call ___djgpp_hw_exception"); +#else /* __DJGPP_MINOR__ >= 2 */ + raise (SIGABRT); +#endif /* __DJGPP_MINOR__ >= 2 */ #endif exit (2); } #endif +/* The following variables are required so that cus-start.el won't + complain about unbound variables. */ +#ifndef HAVE_X_WINDOWS +/* Search path for bitmap files (xfns.c). */ +Lisp_Object Vx_bitmap_file_path; +int x_stretch_cursor_p; +#endif +#ifndef subprocesses +/* Nonzero means delete a process right away if it exits (process.c). */ +static int delete_exited_processes; +#endif + syms_of_msdos () { recent_doskeys = Fmake_vector (make_number (NUM_RECENT_DOSKEYS), Qnil); staticpro (&recent_doskeys); +#ifndef HAVE_X_WINDOWS + help_echo = Qnil; + staticpro (&help_echo); + help_echo_object = Qnil; + staticpro (&help_echo_object); + help_echo_window = Qnil; + staticpro (&help_echo_window); + previous_help_echo = Qnil; + staticpro (&previous_help_echo); + help_echo_pos = -1; + + DEFVAR_LISP ("x-bitmap-file-path", &Vx_bitmap_file_path, + "List of directories to search for bitmap files for X."); + Vx_bitmap_file_path = decode_env_path ((char *) 0, "."); + + DEFVAR_BOOL ("x-stretch-cursor", &x_stretch_cursor_p, + "*Non-nil means draw block cursor as wide as the glyph under it.\n\ +For example, if a block cursor is over a tab, it will be drawn as\n\ +wide as that tab on the display. (No effect on MS-DOS.)"); + x_stretch_cursor_p = 0; + + /* The following two are from xfns.c: */ + Qbar = intern ("bar"); + staticpro (&Qbar); + Qcursor_type = intern ("cursor-type"); + staticpro (&Qcursor_type); + Qreverse = intern ("reverse"); + staticpro (&Qreverse); + + DEFVAR_LISP ("dos-unsupported-char-glyph", &Vdos_unsupported_char_glyph, + "*Glyph to display instead of chars not supported by current codepage.\n\ +\n\ +This variable is used only by MSDOS terminals."); + Vdos_unsupported_char_glyph = '\177'; +#endif +#ifndef subprocesses + DEFVAR_BOOL ("delete-exited-processes", &delete_exited_processes, + "*Non-nil means delete processes immediately when they exit.\n\ +nil means don't delete them until `list-processes' is run."); + delete_exited_processes = 0; +#endif defsubr (&Srecent_doskeys); defsubr (&Smsdos_long_file_names); defsubr (&Smsdos_downcase_filename); + defsubr (&Smsdos_remember_default_colors); + defsubr (&Smsdos_set_mouse_buttons); } #endif /* MSDOS */ +