msdos.c 114 KB
Newer Older
1
/* MS-DOS specific C utilities.          -*- coding: cp850 -*-
2

Paul Eggert's avatar
Paul Eggert committed
3
Copyright (C) 1993-1997, 1999-2016 Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
4 5 6

This file is part of GNU Emacs.

7
GNU Emacs is free software: you can redistribute it and/or modify
Richard M. Stallman's avatar
Richard M. Stallman committed
8
it under the terms of the GNU General Public License as published by
9 10
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Richard M. Stallman's avatar
Richard M. Stallman committed
11 12 13 14 15 16 17

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
18
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
19

20
/* Contributed by Morten Welinder */
Kim F. Storm's avatar
Kim F. Storm committed
21
/* New display, keyboard, and mouse control by Kim F. Storm */
22

23 24 25 26 27 28 29
/* Note: This file MUST use a unibyte encoding, to both display the
   keys on the non-US keyboard layout as their respective labels, and
   provide the correct byte values for the keyboard input to inject
   into Emacs.  See 'struct dos_keyboard_map' below.  As long as there
   are only European keyboard layouts here, we are OK with DOS
   codepage 850 encoding.  */

Richard M. Stallman's avatar
Richard M. Stallman committed
30 31
/* Note: some of the stuff here was taken from end of sysdep.c in demacs. */

Richard M. Stallman's avatar
Richard M. Stallman committed
32
#include <config.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
33 34

#ifdef MSDOS
35
#include <setjmp.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
36 37
#include "lisp.h"
#include <stdio.h>
38
#include <time.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
39 40
#include <sys/param.h>
#include <sys/time.h>
Paul Eggert's avatar
Paul Eggert committed
41
/* gettime and settime in dos.h clash with their namesakes from
42 43 44
   gnulib, so we move out of our way the prototypes in dos.h.  */
#define gettime dos_h_gettime_
#define settime dos_h_settime_
Richard M. Stallman's avatar
Richard M. Stallman committed
45
#include <dos.h>
46 47
#undef gettime
#undef settime
48 49
#include <errno.h>
#include <sys/stat.h>    /* for _fixpath */
50
#include <unistd.h>	 /* for chdir, dup, dup2, etc. */
51
#include <dir.h>	 /* for getdisk */
52
#pragma pack(0)		 /* dir.h does a pack(4), which isn't GCC's default */
Eli Zaretskii's avatar
Eli Zaretskii committed
53 54
#undef opendir
#include <dirent.h>	 /* for opendir */
55
#include <fcntl.h>
56
#include <io.h>		 /* for setmode */
57 58
#include <dpmi.h>	 /* for __dpmi_xxx stuff */
#include <sys/farptr.h>	 /* for _farsetsel, _farnspokeb */
59
#include <libc/dosio.h>  /* for _USE_LFN */
60
#include <conio.h>	 /* for cputs */
61

Richard M. Stallman's avatar
Richard M. Stallman committed
62 63
#include "msdos.h"
#include "systime.h"
64
#include "frame.h"
Richard M. Stallman's avatar
Richard M. Stallman committed
65
#include "termhooks.h"
66
#include "termchar.h"
Morten Welinder's avatar
Morten Welinder committed
67
#include "dispextern.h"
68
#include "dosfns.h"
Morten Welinder's avatar
Morten Welinder committed
69
#include "termopts.h"
70
#include "character.h"
71 72
#include "coding.h"
#include "disptab.h"
Morten Welinder's avatar
Morten Welinder committed
73
#include "window.h"
74
#include "menu.h"
75 76
#include "buffer.h"
#include "commands.h"
77
#include "blockinput.h"
Eli Zaretskii's avatar
Eli Zaretskii committed
78
#include "keyboard.h"
Kenichi Handa's avatar
Kenichi Handa committed
79
#include "intervals.h"
Richard M. Stallman's avatar
Richard M. Stallman committed
80 81 82 83
#include <go32.h>
#include <pc.h>
#include <ctype.h>
/* #include <process.h> */
84 85
/* Damn that local process.h!  Instead we can define P_WAIT and
   spawnve ourselves.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
86
#define P_WAIT 1
87
extern int spawnve (int, const char *, char *const [], char *const []);
Richard M. Stallman's avatar
Richard M. Stallman committed
88

89 90 91 92
#ifndef _USE_LFN
#define _USE_LFN 0
#endif

93 94 95 96
#ifndef _dos_ds
#define _dos_ds _go32_info_block.selector_for_linear_memory
#endif

Richard M. Stallman's avatar
Richard M. Stallman committed
97
#include <signal.h>
98
#include "syssignal.h"
Richard M. Stallman's avatar
Richard M. Stallman committed
99

100 101 102
#include "careadlinkat.h"
#include "allocator.h"

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
#ifndef SYSTEM_MALLOC

#ifdef GNU_MALLOC

/* If other `malloc' than ours is used, force our `sbrk' behave like
   Unix programs expect (resize memory blocks to keep them contiguous).
   If `sbrk' from `ralloc.c' is NOT used, also zero-out sbrk'ed memory,
   because that's what `gmalloc' expects to get.  */
#include <crt0.h>

#ifdef REL_ALLOC
int _crt0_startup_flags = _CRT0_FLAG_UNIX_SBRK;
#else  /* not REL_ALLOC */
int _crt0_startup_flags = (_CRT0_FLAG_UNIX_SBRK | _CRT0_FLAG_FILL_SBRK_MEMORY);
#endif /* not REL_ALLOC */
#endif /* GNU_MALLOC */

#endif /* not SYSTEM_MALLOC */
121

122
/* Return the current timestamp in milliseconds since midnight.  */
123
static unsigned long
124
event_timestamp (void)
125
{
126
  struct timespec t;
127
  unsigned long s;
Eli Zaretskii's avatar
Eli Zaretskii committed
128

129
  gettime (&t);
130 131
  s = t.tv_sec;
  s %= 86400;
132
  s *= 1000;
133
  s += t.tv_nsec * 1000000;
Eli Zaretskii's avatar
Eli Zaretskii committed
134

135 136 137
  return s;
}

Kim F. Storm's avatar
Kim F. Storm committed
138 139 140 141 142 143

/* ------------------------ Mouse control ---------------------------
 *
 * Coordinates are in screen positions and zero based.
 * Mouse buttons are numbered from left to right and also zero based.
 */
Richard M. Stallman's avatar
Richard M. Stallman committed
144

Eli Zaretskii's avatar
Eli Zaretskii committed
145 146 147 148
/* This used to be in termhooks.h, but mainstream Emacs code no longer
   uses it, and it was removed...  */
#define NUM_MOUSE_BUTTONS (5)

Kim F. Storm's avatar
Kim F. Storm committed
149 150
int have_mouse;          /* 0: no, 1: enabled, -1: disabled */
static int mouse_visible;
Richard M. Stallman's avatar
Richard M. Stallman committed
151

Kim F. Storm's avatar
Kim F. Storm committed
152 153
static int mouse_last_x;
static int mouse_last_y;
Richard M. Stallman's avatar
Richard M. Stallman committed
154

Kim F. Storm's avatar
Kim F. Storm committed
155 156
static int mouse_button_translate[NUM_MOUSE_BUTTONS];
static int mouse_button_count;
Richard M. Stallman's avatar
Richard M. Stallman committed
157

Kim F. Storm's avatar
Kim F. Storm committed
158
void
159
mouse_on (void)
Richard M. Stallman's avatar
Richard M. Stallman committed
160 161 162
{
  union REGS regs;

Kim F. Storm's avatar
Kim F. Storm committed
163
  if (have_mouse > 0 && !mouse_visible)
Richard M. Stallman's avatar
Richard M. Stallman committed
164
    {
165 166 167 168
      struct tty_display_info *tty = CURTTY ();

      if (tty->termscript)
	fprintf (tty->termscript, "<M_ON>");
Kim F. Storm's avatar
Kim F. Storm committed
169 170 171
      regs.x.ax = 0x0001;
      int86 (0x33, &regs, &regs);
      mouse_visible = 1;
Richard M. Stallman's avatar
Richard M. Stallman committed
172 173 174
    }
}

Kim F. Storm's avatar
Kim F. Storm committed
175
void
176
mouse_off (void)
Richard M. Stallman's avatar
Richard M. Stallman committed
177
{
Kim F. Storm's avatar
Kim F. Storm committed
178
  union REGS regs;
Richard M. Stallman's avatar
Richard M. Stallman committed
179

Kim F. Storm's avatar
Kim F. Storm committed
180
  if (have_mouse > 0 && mouse_visible)
Richard M. Stallman's avatar
Richard M. Stallman committed
181
    {
182 183 184 185
      struct tty_display_info *tty = CURTTY ();

      if (tty->termscript)
	fprintf (tty->termscript, "<M_OFF>");
Kim F. Storm's avatar
Kim F. Storm committed
186 187 188
      regs.x.ax = 0x0002;
      int86 (0x33, &regs, &regs);
      mouse_visible = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
189 190 191
    }
}

192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
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: ",
212 213 214 215
       doc: /* Set the number of mouse buttons to use by Emacs.
This is useful with mice that report the number of buttons inconsistently,
e.g., if the number of buttons is reported as 3, but Emacs only sees 2 of
them.  This happens with wheeled mice on Windows 9X, for example.  */)
Dan Nicolaescu's avatar
Dan Nicolaescu committed
216
  (Lisp_Object nbuttons)
217
{
218 219
  int n;

220
  CHECK_NUMBER (nbuttons);
221 222
  n = XINT (nbuttons);
  if (n < 2 || n > 3)
223 224 225
    xsignal2 (Qargs_out_of_range,
	      build_string ("only 2 or 3 mouse buttons are supported"),
	      nbuttons);
226
  mouse_setup_buttons (n);
227 228 229
  return Qnil;
}

230 231 232 233 234 235 236 237 238 239 240
static void
mouse_get_xy (int *x, int *y)
{
  union REGS regs;

  regs.x.ax = 0x0003;
  int86 (0x33, &regs, &regs);
  *x = regs.x.cx / 8;
  *y = regs.x.dx / 8;
}

Kim F. Storm's avatar
Kim F. Storm committed
241
void
242
mouse_moveto (int x, int y)
Richard M. Stallman's avatar
Richard M. Stallman committed
243
{
Kim F. Storm's avatar
Kim F. Storm committed
244
  union REGS regs;
245
  struct tty_display_info *tty = CURTTY ();
Richard M. Stallman's avatar
Richard M. Stallman committed
246

247 248
  if (tty->termscript)
    fprintf (tty->termscript, "<M_XY=%dx%d>", x, y);
Kim F. Storm's avatar
Kim F. Storm committed
249 250 251 252
  regs.x.ax = 0x0004;
  mouse_last_x = regs.x.cx = x * 8;
  mouse_last_y = regs.x.dx = y * 8;
  int86 (0x33, &regs, &regs);
Richard M. Stallman's avatar
Richard M. Stallman committed
253 254
}

Kim F. Storm's avatar
Kim F. Storm committed
255
static int
256
mouse_pressed (int b, int *xp, int *yp)
Richard M. Stallman's avatar
Richard M. Stallman committed
257
{
Kim F. Storm's avatar
Kim F. Storm committed
258
  union REGS regs;
Richard M. Stallman's avatar
Richard M. Stallman committed
259

Kim F. Storm's avatar
Kim F. Storm committed
260 261 262 263 264 265 266 267
  if (b >= mouse_button_count)
    return 0;
  regs.x.ax = 0x0005;
  regs.x.bx = mouse_button_translate[b];
  int86 (0x33, &regs, &regs);
  if (regs.x.bx)
    *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
  return (regs.x.bx != 0);
Richard M. Stallman's avatar
Richard M. Stallman committed
268 269
}

Kim F. Storm's avatar
Kim F. Storm committed
270
static int
271
mouse_released (int b, int *xp, int *yp)
Richard M. Stallman's avatar
Richard M. Stallman committed
272 273 274
{
  union REGS regs;

Kim F. Storm's avatar
Kim F. Storm committed
275 276 277 278 279 280 281 282
  if (b >= mouse_button_count)
    return 0;
  regs.x.ax = 0x0006;
  regs.x.bx = mouse_button_translate[b];
  int86 (0x33, &regs, &regs);
  if (regs.x.bx)
    *xp = regs.x.cx / 8, *yp = regs.x.dx / 8;
  return (regs.x.bx != 0);
Richard M. Stallman's avatar
Richard M. Stallman committed
283 284
}

285
static int
286
mouse_button_depressed (int b, int *xp, int *yp)
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
{
  union REGS regs;

  if (b >= mouse_button_count)
    return 0;
  regs.x.ax = 0x0003;
  int86 (0x33, &regs, &regs);
  if ((regs.x.bx & (1 << mouse_button_translate[b])) != 0)
    {
      *xp = regs.x.cx / 8;
      *yp = regs.x.dx / 8;
      return 1;
    }
  return 0;
}

Kim F. Storm's avatar
Kim F. Storm committed
303
void
Dmitry Antipov's avatar
Dmitry Antipov committed
304
mouse_get_pos (struct frame **f, int insist, Lisp_Object *bar_window,
305
	       enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
306
	       Time *time)
Kim F. Storm's avatar
Kim F. Storm committed
307 308
{
  int ix, iy;
309 310 311 312 313
  Lisp_Object frame, tail;

  /* Clear the mouse-moved flag for every frame on this display.  */
  FOR_EACH_FRAME (tail, frame)
    XFRAME (frame)->mouse_moved = 0;
Kim F. Storm's avatar
Kim F. Storm committed
314

Juanma Barranquero's avatar
Juanma Barranquero committed
315
  *f = SELECTED_FRAME ();
Kim F. Storm's avatar
Kim F. Storm committed
316 317 318
  *bar_window = Qnil;
  mouse_get_xy (&ix, &iy);
  *time = event_timestamp ();
319 320
  *x = make_number (mouse_last_x = ix);
  *y = make_number (mouse_last_y = iy);
Kim F. Storm's avatar
Kim F. Storm committed
321
}
Richard M. Stallman's avatar
Richard M. Stallman committed
322

Kim F. Storm's avatar
Kim F. Storm committed
323
static void
324
mouse_check_moved (void)
Richard M. Stallman's avatar
Richard M. Stallman committed
325
{
326
  int x, y;
Richard M. Stallman's avatar
Richard M. Stallman committed
327

Kim F. Storm's avatar
Kim F. Storm committed
328
  mouse_get_xy (&x, &y);
Juanma Barranquero's avatar
Juanma Barranquero committed
329
  SELECTED_FRAME ()->mouse_moved |= (x != mouse_last_x || y != mouse_last_y);
Kim F. Storm's avatar
Kim F. Storm committed
330 331 332
  mouse_last_x = x;
  mouse_last_y = y;
}
Richard M. Stallman's avatar
Richard M. Stallman committed
333

Eli Zaretskii's avatar
Eli Zaretskii committed
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
/* 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);
    }
}

Kim F. Storm's avatar
Kim F. Storm committed
350
void
351
mouse_init (void)
Kim F. Storm's avatar
Kim F. Storm committed
352 353
{
  union REGS regs;
354
  struct tty_display_info *tty = CURTTY ();
355

356 357
  if (tty->termscript)
    fprintf (tty->termscript, "<M_INIT>");
Richard M. Stallman's avatar
Richard M. Stallman committed
358

Kim F. Storm's avatar
Kim F. Storm committed
359 360
  regs.x.ax = 0x0021;
  int86 (0x33, &regs, &regs);
361

362 363 364 365
  /* 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.  */
Eli Zaretskii's avatar
Eli Zaretskii committed
366
  mouse_clear_clicks ();
367

Kim F. Storm's avatar
Kim F. Storm committed
368 369 370 371
  regs.x.ax = 0x0007;
  regs.x.cx = 0;
  regs.x.dx = 8 * (ScreenCols () - 1);
  int86 (0x33, &regs, &regs);
Richard M. Stallman's avatar
Richard M. Stallman committed
372

Kim F. Storm's avatar
Kim F. Storm committed
373 374 375 376
  regs.x.ax = 0x0008;
  regs.x.cx = 0;
  regs.x.dx = 8 * (ScreenRows () - 1);
  int86 (0x33, &regs, &regs);
Richard M. Stallman's avatar
Richard M. Stallman committed
377

Kim F. Storm's avatar
Kim F. Storm committed
378 379 380
  mouse_moveto (0, 0);
  mouse_visible = 0;
}
Richard M. Stallman's avatar
Richard M. Stallman committed
381

Kim F. Storm's avatar
Kim F. Storm committed
382 383 384
/* ------------------------- Screen control ----------------------
 *
 */
385

Kim F. Storm's avatar
Kim F. Storm committed
386
static int internal_terminal = 0;
387

Kim F. Storm's avatar
Kim F. Storm committed
388 389 390
#ifndef HAVE_X_WINDOWS
extern unsigned char ScreenAttrib;
static int screen_face;
391

Kim F. Storm's avatar
Kim F. Storm committed
392 393 394
static int screen_size_X;
static int screen_size_Y;
static int screen_size;
Richard M. Stallman's avatar
Richard M. Stallman committed
395

Kim F. Storm's avatar
Kim F. Storm committed
396 397 398 399
static int current_pos_X;
static int current_pos_Y;
static int new_pos_X;
static int new_pos_Y;
Richard M. Stallman's avatar
Richard M. Stallman committed
400

Kim F. Storm's avatar
Kim F. Storm committed
401 402 403 404 405
static void *startup_screen_buffer;
static int startup_screen_size_X;
static int startup_screen_size_Y;
static int startup_pos_X;
static int startup_pos_Y;
406
static unsigned char startup_screen_attrib;
Richard M. Stallman's avatar
Richard M. Stallman committed
407

408 409
static clock_t startup_time;

Kim F. Storm's avatar
Kim F. Storm committed
410
static int term_setup_done;
Richard M. Stallman's avatar
Richard M. Stallman committed
411

412 413
static unsigned short outside_cursor;

414
/* The only display since MS-DOS does not support multiple ones.  */
415
struct tty_display_info the_only_display_info;
Richard M. Stallman's avatar
Richard M. Stallman committed
416

417 418 419 420 421 422 423 424
/* 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;
Kenichi Handa's avatar
Kenichi Handa committed
425 426
extern Lisp_Object Qcursor_type;
extern Lisp_Object Qbar, Qhbar;
427

Juanma Barranquero's avatar
Juanma Barranquero committed
428
/* The screen colors of the current frame, which serve as the default
429 430 431
   colors for newly-created frames.  */
static int initial_screen_colors[2];

432 433 434 435 436 437 438
/* 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;

439
  if (offset < 0 || count < 0)	/* paranoia; invalid values crash DOS/V */
440 441
    return;

442 443 444 445 446 447 448
  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, &regs);
}

449
static void
450
dos_direct_output (int y, int x, char *buf, int len)
Richard M. Stallman's avatar
Richard M. Stallman committed
451
{
452 453
  int t0 = 2 * (x + y * screen_size_X);
  int t = t0 + (int) ScreenPrimary;
454
  int l0 = len;
455 456 457 458

  /* This is faster.  */
  for (_farsetsel (_dos_ds); --len >= 0; t += 2, buf++)
    _farnspokeb (t, *buf);
459 460 461

  if (screen_virtual_segment)
    dosv_refresh_virtual_screen (t0, l0);
Kim F. Storm's avatar
Kim F. Storm committed
462 463 464 465 466
}
#endif

#ifndef HAVE_X_WINDOWS

467 468
static int blink_bit = -1;	/* the state of the blink bit at startup */

469 470 471 472 473 474
/* Enable bright background colors.  */
static void
bright_bg (void)
{
  union REGS regs;

475 476 477 478 479
  /* 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;

480 481 482 483 484
  regs.h.bl = 0;
  regs.x.ax = 0x1003;
  int86 (0x10, &regs, &regs);
}

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
/* 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, &regs, &regs);
    }
}

500 501 502 503 504 505 506 507 508 509 510 511 512
/* Return non-zero if the system has a VGA adapter.  */
static int
vga_installed (void)
{
  union REGS regs;

  regs.x.ax = 0x1a00;
  int86 (0x10, &regs, &regs);
  if (regs.h.al == 0x1a && regs.h.bl > 5 && regs.h.bl < 13)
    return 1;
  return 0;
}

513 514
/* Set the screen dimensions so that it can show no less than
   ROWS x COLS frame.  */
515

516
void
517
dos_set_window_size (int *rows, int *cols)
518 519 520
{
  char video_name[30];
  union REGS regs;
Eli Zaretskii's avatar
Eli Zaretskii committed
521 522
  Lisp_Object video_mode;
  int video_mode_value, have_vga = 0;
523 524 525 526 527 528
  int current_rows = ScreenRows (), current_cols = ScreenCols ();

  if (*rows == current_rows && *cols == current_cols)
    return;

  mouse_off ();
529
  have_vga = vga_installed ();
530

531
  /* If the user specified a special video mode for these dimensions,
532
     use that mode.  */
Paul Eggert's avatar
Paul Eggert committed
533 534
  video_mode
    = Fsymbol_value (Fintern_soft (make_formatted_string
535 536
				   (video_name, "screen-dimensions-%dx%d",
				    *rows, *cols), Qnil));
537 538 539 540 541 542

  if (INTEGERP (video_mode)
      && (video_mode_value = XINT (video_mode)) > 0)
    {
      regs.x.ax = video_mode_value;
      int86 (0x10, &regs, &regs);
543 544 545 546 547 548 549 550 551

      if (have_mouse)
	{
	  /* Must hardware-reset the mouse, or else it won't update
	     its notion of screen dimensions for some non-standard
	     video modes.  This is *painfully* slow...  */
	  regs.x.ax = 0;
	  int86 (0x33, &regs, &regs);
	}
552 553 554 555 556 557 558
    }

  /* Find one of the dimensions supported by standard EGA/VGA
     which gives us at least the required dimensions.  */
  else
    {
      static struct {
Eli Zaretskii's avatar
Eli Zaretskii committed
559
	int rows, need_vga;
560 561 562 563 564 565 566 567 568 569
      }	std_dimension[] = {
	  {25, 0},
	  {28, 1},
	  {35, 0},
	  {40, 1},
	  {43, 0},
	  {50, 1}
      };
      int i = 0;

570
      while (i < ARRAYELTS (std_dimension))
571 572 573 574 575 576
	{
	 if (std_dimension[i].need_vga <= have_vga
	     && std_dimension[i].rows >= *rows)
	   {
	     if (std_dimension[i].rows != current_rows
		 || *cols != current_cols)
577
	       _set_screen_lines (std_dimension[i].rows);
578 579
	     break;
	   }
580
	 i++;
581 582 583 584 585 586 587 588 589 590 591 592 593
	}
    }


  if (have_mouse)
    {
      mouse_init ();
      mouse_on ();
    }

  /* Tell the caller what dimensions have been REALLY set.  */
  *rows = ScreenRows ();
  *cols = ScreenCols ();
594

595 596 597 598 599
  /* Update Emacs' notion of screen dimensions.  */
  screen_size_X = *cols;
  screen_size_Y = *rows;
  screen_size = *cols * *rows;

600 601 602
  /* If the dimensions changed, the mouse highlight info is invalid.  */
  if (current_rows != *rows || current_cols != *cols)
    {
Juanma Barranquero's avatar
Juanma Barranquero committed
603
      struct frame *f = SELECTED_FRAME ();
604 605
      Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
      Lisp_Object window = hlinfo->mouse_face_window;
606 607

      if (! NILP (window) && XFRAME (XWINDOW (window)->frame) == f)
608
	reset_mouse_highlight (hlinfo);
609 610
    }

611 612
  /* Enable bright background colors.  */
  bright_bg ();
613 614 615 616 617

  /* 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);
618 619
}

620 621
/* If we write a character in the position where the mouse is,
   the mouse cursor may need to be refreshed.  */
622 623

static void
624
mouse_off_maybe (void)
625
{
Kim F. Storm's avatar
Kim F. Storm committed
626
  int x, y;
Eli Zaretskii's avatar
Eli Zaretskii committed
627

Kim F. Storm's avatar
Kim F. Storm committed
628 629
  if (!mouse_visible)
    return;
Eli Zaretskii's avatar
Eli Zaretskii committed
630

Kim F. Storm's avatar
Kim F. Storm committed
631 632 633
  mouse_get_xy (&x, &y);
  if (y != new_pos_Y || x < new_pos_X)
    return;
Eli Zaretskii's avatar
Eli Zaretskii committed
634

Kim F. Storm's avatar
Kim F. Storm committed
635 636 637
  mouse_off ();
}

638 639 640 641 642 643 644 645 646 647 648 649 650
#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)
{
  unsigned desired_cursor;
  __dpmi_regs regs;
  int max_line, top_line, bot_line;
651
  struct tty_display_info *tty = FRAME_TTY (f);
652 653 654 655

  /* Avoid the costly BIOS call if F isn't the currently selected
     frame.  Allow for NULL as unconditionally meaning the selected
     frame.  */
Juanma Barranquero's avatar
Juanma Barranquero committed
656
  if (f && f != SELECTED_FRAME ())
657 658
    return;

659 660
  if (tty->termscript)
    fprintf (tty->termscript, "\nCURSOR SHAPE=(%d,%d)", start_line, width);
661

662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
  /* 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, &regs);
}

static void
IT_set_cursor_type (struct frame *f, Lisp_Object cursor_type)
{
Eli Zaretskii's avatar
Eli Zaretskii committed
729
  if (EQ (cursor_type, Qbar) || EQ (cursor_type, Qhbar))
730 731 732 733
    {
      /* Just BAR means the normal EGA/VGA cursor.  */
      msdos_set_cursor_shape (f, DEFAULT_CURSOR_START, DEFAULT_CURSOR_WIDTH);
    }
Eli Zaretskii's avatar
Eli Zaretskii committed
734 735 736
  else if (CONSP (cursor_type)
	   && (EQ (XCAR (cursor_type), Qbar)
	       || EQ (XCAR (cursor_type), Qhbar)))
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
    {
      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
760 761 762 763 764 765
    {
      /* 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);
    }
766 767
}

768
static void
769
IT_ring_bell (struct frame *f)
Kim F. Storm's avatar
Kim F. Storm committed
770 771
{
  if (visible_bell)
772
    {
Kim F. Storm's avatar
Kim F. Storm committed
773 774
      mouse_off ();
      ScreenVisualBell ();
775
    }
Kim F. Storm's avatar
Kim F. Storm committed
776
  else
777 778 779 780 781 782
    {
      union REGS inregs, outregs;
      inregs.h.ah = 2;
      inregs.h.dl = 7;
      intdos (&inregs, &outregs);
    }
783 784
}

785 786 787 788 789
/* 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.  */
Kim F. Storm's avatar
Kim F. Storm committed
790 791 792
static void
IT_set_face (int face)
{
Juanma Barranquero's avatar
Juanma Barranquero committed
793
  struct frame *sf = SELECTED_FRAME ();
794 795 796
  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;
797
  struct tty_display_info *tty = FRAME_TTY (sf);
Kim F. Storm's avatar
Kim F. Storm committed
798

799
  if (!fp)
800
    {
801
      fp = dfp;
802 803 804
      /* The default face for the frame should always be realized and
	 cached.  */
      if (!fp)
805
	emacs_abort ();
806
    }
Kim F. Storm's avatar
Kim F. Storm committed
807
  screen_face = face;
808 809
  fg = fp->foreground;
  bg = fp->background;
810 811
  dflt_fg = dfp->foreground;
  dflt_bg = dfp->background;
812

813 814 815 816
  /* Don't use invalid colors.  In particular, FACE_TTY_DEFAULT_* colors
     mean use the colors of the default face.  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.  */
817
  if (fg == FACE_TTY_DEFAULT_COLOR || fg == FACE_TTY_DEFAULT_FG_COLOR)
818
    fg = FRAME_FOREGROUND_PIXEL (sf);
819
  else if (fg == FACE_TTY_DEFAULT_BG_COLOR)
820 821 822
    fg = FRAME_BACKGROUND_PIXEL (sf);
  if (bg == FACE_TTY_DEFAULT_COLOR || bg == FACE_TTY_DEFAULT_BG_COLOR)
    bg = FRAME_BACKGROUND_PIXEL (sf);
823
  else if (bg == FACE_TTY_DEFAULT_FG_COLOR)
824 825 826
    bg = FRAME_FOREGROUND_PIXEL (sf);

  /* Make sure highlighted lines really stand out, come what may.  */
827
  if (fp->tty_reverse_p && (fg == dflt_fg && bg == dflt_bg))
828 829 830 831 832 833
    {
      unsigned long tem = fg;

      fg = bg;
      bg = tem;
    }
834 835 836 837 838 839 840 841
  /* If the user requested inverse video, obey.  */
  if (inverse_video)
    {
      unsigned long tem2 = fg;

      fg = bg;
      bg = tem2;
    }
842
  if (tty->termscript)
843
    fprintf (tty->termscript, "<FACE %d: %lu/%lu[FG:%lu/BG:%lu]>", face,
844
	     fp->foreground, fp->background, fg, bg);
845 846 847 848 849 850 851 852 853 854
  if (fg >= 0 && fg < 16)
    {
      ScreenAttrib &= 0xf0;
      ScreenAttrib |= fg;
    }
  if (bg >= 0 && bg < 16)
    {
      ScreenAttrib &= 0x0f;
      ScreenAttrib |= ((bg & 0x0f) << 4);
    }
Kim F. Storm's avatar
Kim F. Storm committed
855 856
}

Eli Zaretskii's avatar
Eli Zaretskii committed
857 858
/* According to RBIL (INTERRUP.A, V-1000), 160 is the maximum possible
   width of a DOS display in any known text mode.  We multiply by 2 to
Juanma Barranquero's avatar
Juanma Barranquero committed
859
   accommodate the screen attribute byte.  */
Eli Zaretskii's avatar
Eli Zaretskii committed
860 861
#define MAX_SCREEN_BUF 160*2

862 863
extern unsigned char *encode_terminal_code (struct glyph *, int,
					    struct coding_system *);
864

865
static void
866
IT_write_glyphs (struct frame *f, struct glyph *str, int str_len)
Kim F. Storm's avatar
Kim F. Storm committed
867
{
Eli Zaretskii's avatar
Eli Zaretskii committed
868
  unsigned char screen_buf[MAX_SCREEN_BUF], *screen_bp, *bp;
869
  int offset = 2 * (new_pos_X + screen_size_X * new_pos_Y);
870
  register int sl = str_len;
871
  struct tty_display_info *tty = FRAME_TTY (f);
872
  struct frame *sf;
873
  unsigned char *conversion_buffer;
Morten Welinder's avatar
Morten Welinder committed
874

875 876 877 878 879 880 881
  /* 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 = FRAME_TERMINAL_CODING (f);

  if (!(coding->common_flags & CODING_REQUIRE_ENCODING_MASK))
    coding = &safe_terminal_coding;
882

883
  if (str_len <= 0) return;
Eli Zaretskii's avatar
Eli Zaretskii committed
884

Juanma Barranquero's avatar
Juanma Barranquero committed
885
  sf = SELECTED_FRAME ();
886 887 888 889 890 891 892

  /* 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);
Eli Zaretskii's avatar
Eli Zaretskii committed
893

894 895
  /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
     the tail.  */
896
  coding->mode &= ~CODING_MODE_LAST_BLOCK;
Eli Zaretskii's avatar
Eli Zaretskii committed
897
  screen_bp = &screen_buf[0];
898
  while (sl > 0)
899
    {
900
      int cf;
Eli Zaretskii's avatar
Eli Zaretskii committed
901
      int n;
902

Eli Zaretskii's avatar
Eli Zaretskii committed
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
      /* 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 */

      /* Identify a run of glyphs with the same face.  */
      for (n = 1; n < sl; ++n)
	if (str[n].face_id != cf)
	  break;

      if (n >= sl)
	/* This is the last glyph.  */
	coding->mode |= CODING_MODE_LAST_BLOCK;

      conversion_buffer = encode_terminal_code (str, n, coding);
      if (coding->produced > 0)
920
	{
Eli Zaretskii's avatar
Eli Zaretskii committed
921 922
	  /* Copy the encoded bytes to the screen buffer.  */
	  for (bp = conversion_buffer; coding->produced--; bp++)
923
	    {
Eli Zaretskii's avatar
Eli Zaretskii committed
924 925 926
	      /* Paranoia: discard bytes that would overrun the end of
		 the screen buffer.  */
	      if (screen_bp - screen_buf <= MAX_SCREEN_BUF - 2)
927
		{
Eli Zaretskii's avatar
Eli Zaretskii committed
928 929
		  *screen_bp++ = (unsigned char)*bp;
		  *screen_bp++ = ScreenAttrib;
930
		}
Eli Zaretskii's avatar
Eli Zaretskii committed
931 932
	      if (tty->termscript)
		fputc (*bp, tty->termscript);
933 934
	    }
	}
Eli Zaretskii's avatar
Eli Zaretskii committed
935 936 937
      /* Update STR and its remaining length.  */
      str += n;
      sl -= n;
938 939
    }

Eli Zaretskii's avatar
Eli Zaretskii committed
940
  /* Dump whatever we have in the screen buffer.  */
Kim F. Storm's avatar
Kim F. Storm committed
941
  mouse_off_maybe ();
942
  dosmemput (screen_buf, screen_bp - screen_buf, (int)ScreenPrimary + offset);
943
  if (screen_virtual_segment)
944 945
    dosv_refresh_virtual_screen (offset, (screen_bp - screen_buf) / 2);
  new_pos_X += (screen_bp - screen_buf) / 2;
Kim F. Storm's avatar
Kim F. Storm committed
946
}
947

948 949 950 951 952 953
/************************************************************************
			  Mouse Highlight (and friends..)
 ************************************************************************/

static int mouse_preempted = 0;	/* non-zero when XMenu gobbles mouse events */

954 955
int
popup_activated (void)
956
{
957
  return mouse_preempted;
958 959
}

960
/* Draw TEXT_AREA glyphs between START and END of glyph row ROW on
961 962
   window W.  X is relative to TEXT_AREA in W.  HL is a face override
   for drawing the glyphs.  */
963
void
964 965 966
tty_draw_row_with_mouse_face (struct window *w, struct glyph_row *row,
			      int start_hpos, int end_hpos,
			      enum draw_glyphs_face hl)
967 968
{
  struct frame *f = XFRAME (WINDOW_FRAME (w));
969
  struct tty_display_info *tty = FRAME_TTY (f);
970
  Mouse_HLInfo *hlinfo = &tty->mouse_highlight;
971

972
  if (hl == DRAW_MOUSE_FACE)
973
    {
974 975 976 977 978
      int vpos = row->y + WINDOW_TOP_EDGE_Y (w);
      int kstart = start_hpos + WINDOW_LEFT_EDGE_X (w);
      int nglyphs = end_hpos - start_hpos;
      int offset = ScreenPrimary + 2*(vpos*screen_size_X + kstart) + 1;
      int start_offset = offset;
979

980 981 982
      if (tty->termscript)
	fprintf (tty->termscript, "\n<MH+ %d-%d:%d>",
		 kstart, kstart + nglyphs - 1, vpos);
Eli Zaretskii's avatar
Eli Zaretskii committed
983

984
      mouse_off ();
985
      IT_set_face (hlinfo->mouse_face_face_id);
986 987 988 989 990 991 992
      /* Since we are going to change only the _colors_ of already
	 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--)
993
	{
994 995
	  _farnspokeb (offset, ScreenAttrib);
	  offset += 2;
996
	}
997 998 999
      if (screen_virtual_segment)
	dosv_refresh_virtual_screen (start_offset, end_hpos - start_hpos);
      mouse_on ();
1000
    }
1001
  else if (hl == DRAW_NORMAL_TEXT)
1002