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

3
Copyright (C) 1993-1997, 1999-2014 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 75
#include "buffer.h"
#include "commands.h"
76
#include "blockinput.h"
Eli Zaretskii's avatar
Eli Zaretskii committed
77
#include "keyboard.h"
Kenichi Handa's avatar
Kenichi Handa committed
78
#include "intervals.h"
Richard M. Stallman's avatar
Richard M. Stallman committed
79 80 81 82
#include <go32.h>
#include <pc.h>
#include <ctype.h>
/* #include <process.h> */
83 84
/* Damn that local process.h!  Instead we can define P_WAIT and
   spawnve ourselves.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
85
#define P_WAIT 1
86
extern int spawnve (int, const char *, char *const [], char *const []);
Richard M. Stallman's avatar
Richard M. Stallman committed
87

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

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

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

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

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
#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 */
120

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

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

134 135 136
  return s;
}

Kim F. Storm's avatar
Kim F. Storm committed
137 138 139 140 141 142

/* ------------------------ 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
143

Eli Zaretskii's avatar
Eli Zaretskii committed
144 145 146 147
/* 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
148 149
int have_mouse;          /* 0: no, 1: enabled, -1: disabled */
static int mouse_visible;
Richard M. Stallman's avatar
Richard M. Stallman committed
150

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

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

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

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

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

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

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

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

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
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: ",
211 212 213 214
       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
215
  (Lisp_Object nbuttons)
216
{
217 218
  int n;

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

229 230 231 232 233 234 235 236 237 238 239
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
240
void
241
mouse_moveto (int x, int y)
Richard M. Stallman's avatar
Richard M. Stallman committed
242
{
Kim F. Storm's avatar
Kim F. Storm committed
243
  union REGS regs;
244
  struct tty_display_info *tty = CURTTY ();
Richard M. Stallman's avatar
Richard M. Stallman committed
245

246 247
  if (tty->termscript)
    fprintf (tty->termscript, "<M_XY=%dx%d>", x, y);
Kim F. Storm's avatar
Kim F. Storm committed
248 249 250 251
  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
252 253
}

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

Kim F. Storm's avatar
Kim F. Storm committed
259 260 261 262 263 264 265 266
  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
267 268
}

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

Kim F. Storm's avatar
Kim F. Storm committed
274 275 276 277 278 279 280 281
  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
282 283
}

284
static int
285
mouse_button_depressed (int b, int *xp, int *yp)
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
{
  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
302
void
Dmitry Antipov's avatar
Dmitry Antipov committed
303
mouse_get_pos (struct frame **f, int insist, Lisp_Object *bar_window,
304
	       enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
305
	       Time *time)
Kim F. Storm's avatar
Kim F. Storm committed
306 307
{
  int ix, iy;
308 309 310 311 312
  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
313

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

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

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

Eli Zaretskii's avatar
Eli Zaretskii committed
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
/* 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
349
void
350
mouse_init (void)
Kim F. Storm's avatar
Kim F. Storm committed
351 352
{
  union REGS regs;
353
  struct tty_display_info *tty = CURTTY ();
354

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

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

361 362 363 364
  /* 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
365
  mouse_clear_clicks ();
366

Kim F. Storm's avatar
Kim F. Storm committed
367 368 369 370
  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
371

Kim F. Storm's avatar
Kim F. Storm committed
372 373 374 375
  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
376

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

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

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

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

Kim F. Storm's avatar
Kim F. Storm committed
395 396 397 398
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
399

Kim F. Storm's avatar
Kim F. Storm committed
400 401 402 403 404
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;
405
static unsigned char startup_screen_attrib;
Richard M. Stallman's avatar
Richard M. Stallman committed
406

407 408
static clock_t startup_time;

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

411 412
static unsigned short outside_cursor;

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

416 417 418 419 420 421 422 423
/* 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
424 425
extern Lisp_Object Qcursor_type;
extern Lisp_Object Qbar, Qhbar;
426

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

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

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

441 442 443 444 445 446 447
  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);
}

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

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

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

#ifndef HAVE_X_WINDOWS

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

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

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

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

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

499 500 501 502 503 504 505 506 507 508 509 510 511
/* 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;
}

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

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

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

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

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

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

      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);
	}
551 552 553 554 555 556 557
    }

  /* 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
558
	int rows, need_vga;
559 560 561 562 563 564 565 566 567 568
      }	std_dimension[] = {
	  {25, 0},
	  {28, 1},
	  {35, 0},
	  {40, 1},
	  {43, 0},
	  {50, 1}
      };
      int i = 0;

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


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

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

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

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

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

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

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

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

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

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

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

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

637 638 639 640 641 642 643 644 645 646 647 648 649
#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;
650
  struct tty_display_info *tty = FRAME_TTY (f);
651 652 653 654

  /* 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
655
  if (f && f != SELECTED_FRAME ())
656 657
    return;

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

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
  /* 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
728
  if (EQ (cursor_type, Qbar) || EQ (cursor_type, Qhbar))
729 730 731 732
    {
      /* 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
733 734 735
  else if (CONSP (cursor_type)
	   && (EQ (XCAR (cursor_type), Qbar)
	       || EQ (XCAR (cursor_type), Qhbar)))
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
    {
      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
759 760 761 762 763 764
    {
      /* 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);
    }
765 766
}

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

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

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

812 813 814 815
  /* 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.  */
816
  if (fg == FACE_TTY_DEFAULT_COLOR || fg == FACE_TTY_DEFAULT_FG_COLOR)
817
    fg = FRAME_FOREGROUND_PIXEL (sf);
818
  else if (fg == FACE_TTY_DEFAULT_BG_COLOR)
819 820 821
    fg = FRAME_BACKGROUND_PIXEL (sf);
  if (bg == FACE_TTY_DEFAULT_COLOR || bg == FACE_TTY_DEFAULT_BG_COLOR)
    bg = FRAME_BACKGROUND_PIXEL (sf);
822
  else if (bg == FACE_TTY_DEFAULT_FG_COLOR)
823 824 825
    bg = FRAME_FOREGROUND_PIXEL (sf);

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

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

      fg = bg;
      bg = tem2;
    }
841
  if (tty->termscript)
842
    fprintf (tty->termscript, "<FACE %d: %lu/%lu[FG:%lu/BG:%lu]>", face,
843
	     fp->foreground, fp->background, fg, bg);
844 845 846 847 848 849 850 851 852 853
  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
854 855
}

Eli Zaretskii's avatar
Eli Zaretskii committed
856 857
/* 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
858
   accommodate the screen attribute byte.  */
Eli Zaretskii's avatar
Eli Zaretskii committed
859 860
#define MAX_SCREEN_BUF 160*2

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

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

874 875 876 877 878 879 880
  /* 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;
881

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

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

  /* 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
892

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

Eli Zaretskii's avatar
Eli Zaretskii committed
902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
      /* 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)
919
	{
Eli Zaretskii's avatar
Eli Zaretskii committed
920 921
	  /* Copy the encoded bytes to the screen buffer.  */
	  for (bp = conversion_buffer; coding->produced--; bp++)
922
	    {
Eli Zaretskii's avatar
Eli Zaretskii committed
923 924 925
	      /* Paranoia: discard bytes that would overrun the end of
		 the screen buffer.  */
	      if (screen_bp - screen_buf <= MAX_SCREEN_BUF - 2)
926
		{
Eli Zaretskii's avatar
Eli Zaretskii committed
927 928
		  *screen_bp++ = (unsigned char)*bp;
		  *screen_bp++ = ScreenAttrib;
929
		}
Eli Zaretskii's avatar
Eli Zaretskii committed
930 931
	      if (tty->termscript)
		fputc (*bp, tty->termscript);
932 933
	    }
	}
Eli Zaretskii's avatar
Eli Zaretskii committed
934 935 936
      /* Update STR and its remaining length.  */
      str += n;
      sl -= n;
937 938
    }

Eli Zaretskii's avatar
Eli Zaretskii committed
939
  /* Dump whatever we have in the screen buffer.  */
Kim F. Storm's avatar
Kim F. Storm committed
940
  mouse_off_maybe ();
941
  dosmemput (screen_buf, screen_bp - screen_buf, (int)ScreenPrimary + offset);
942
  if (screen_virtual_segment)
943 944
    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
945
}
946

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

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

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

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

971
  if (hl == DRAW_MOUSE_FACE)
972
    {
973 974 975 976 977
      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;
978

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

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