(shell-command-on-region): Doc fix.
[bpt/emacs.git] / src / msdos.c
index 09bcba5..f70c196 100644 (file)
@@ -214,6 +214,25 @@ mouse_released (b, xp, yp)
   return (regs.x.bx != 0);
 }
 
+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, &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;
+}
+
 void
 mouse_get_pos (f, insist, bar_window, part, x, y, time)
      FRAME_PTR *f;
@@ -252,6 +271,7 @@ void
 mouse_init ()
 {
   union REGS regs;
+  int b;
 
   if (termscript)
     fprintf (termscript, "<M_INIT>");
@@ -259,6 +279,18 @@ mouse_init ()
   regs.x.ax = 0x0021;
   int86 (0x33, &regs, &regs);
 
+  /* 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.  */
+  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);
+    }
+
   regs.x.ax = 0x0007;
   regs.x.cx = 0;
   regs.x.dx = 8 * (ScreenCols () - 1);
@@ -600,8 +632,8 @@ mouse_off_maybe ()
   mouse_off ();
 }
 
-static
-IT_ring_bell ()
+static void
+IT_ring_bell (void)
 {
   if (visible_bell)
     {
@@ -636,7 +668,7 @@ IT_set_face (int face)
   ScreenAttrib = (FACE_BACKGROUND (fp) << 4) | FACE_FOREGROUND (fp);
 }
 
-static
+static void
 IT_write_glyphs (GLYPH *str, int len)
 {
   int newface;
@@ -669,8 +701,8 @@ IT_write_glyphs (GLYPH *str, int len)
   new_pos_X += len;
 }
 
-static
-IT_clear_end_of_line (first_unused)
+static void
+IT_clear_end_of_line (int first_unused)
 {
   char *spaces, *sp;
   int i, j;
@@ -694,7 +726,7 @@ IT_clear_end_of_line (first_unused)
     dosv_refresh_virtual_screen (offset, i / 2);
 }
 
-static
+static void
 IT_clear_screen (void)
 {
   if (termscript)
@@ -707,7 +739,7 @@ IT_clear_screen (void)
   new_pos_X = new_pos_Y = 0;
 }
 
-static
+static void
 IT_clear_to_end (void)
 {
   if (termscript)
@@ -720,7 +752,7 @@ IT_clear_to_end (void)
   }
 }
 
-static
+static void
 IT_cursor_to (int y, int x)
 {
   if (termscript)
@@ -765,20 +797,48 @@ IT_display_cursor (int on)
    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
-IT_cmgoto (f)
-     FRAME_PTR f;
+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 = MODIFF == unchanged_modified;
-
-  /* If we are in the echo area, put the cursor at the end of text.  */
+  static int previous_pos_X = -1;
+
+  /* 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)
     {
-      new_pos_X = FRAME_DESIRED_GLYPHS (f)->used[new_pos_Y];
-      update_cursor_pos = 1;
+      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;
+       }
     }
 
   if (update_cursor_pos
@@ -798,16 +858,15 @@ IT_cmgoto (f)
     mouse_on ();
 }
 
-static
-IT_reassert_line_highlight (new, vpos)
-     int new, vpos;
+static void
+IT_reassert_line_highlight (int new, int vpos)
 {
   highlight = new;
   IT_set_face (0); /* To possibly clear the highlighting.  */
 }
 
-static
-IT_change_line_highlight (new_highlight, vpos, first_unused_hpos)
+static void
+IT_change_line_highlight (int new_highlight, int vpos, int first_unused_hpos)
 {
   highlight = new_highlight;
   IT_set_face (0); /* To possibly clear the highlighting.  */
@@ -815,16 +874,16 @@ IT_change_line_highlight (new_highlight, vpos, first_unused_hpos)
   IT_clear_end_of_line (first_unused_hpos);
 }
 
-static
-IT_update_begin ()
+static void
+IT_update_begin (struct frame *foo)
 {
   highlight = 0;
   IT_set_face (0); /* To possibly clear the highlighting.  */
   screen_face = -1;
 }
 
-static
-IT_update_end ()
+static void
+IT_update_end (struct frame *foo)
 {
 }
 
@@ -846,7 +905,7 @@ 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)
 {
   if (termscript)
@@ -909,7 +968,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;
@@ -981,8 +1040,8 @@ IT_reset_terminal_modes (void)
   term_setup_done = 0;
 }
 
-static
-IT_set_terminal_window (void)
+static void
+IT_set_terminal_window (int foo)
 {
 }
 
@@ -1274,11 +1333,14 @@ dos_set_keyboard (code, always)
      int always;
 {
   int i;
-  union REGS regs;
+  _go32_dpmi_registers regs;
 
-  /* See if Keyb.Com is installed (for international keyboard support).  */
+  /* 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;
-  int86 (0x2f, &regs, &regs);
+  regs.x.ss = regs.x.sp = regs.x.flags = 0;
+  _go32_dpmi_simulate_int (0x2f, &regs);
   if (regs.h.al == 0xff)
     international_keyboard = 1;
 
@@ -1661,6 +1723,7 @@ and then the scan code.")
 /* Get a char from keyboard.  Function keys are put into the event queue.  */
 
 extern void kbd_buffer_store_event (struct input_event *);
+static int mouse_preempted = 0;        /* non-zero when XMenu gobbles mouse events */
 
 static int
 dos_rawgetc ()
@@ -1868,7 +1931,7 @@ dos_rawgetc ()
       kbd_buffer_store_event (&event);
     }
 
-  if (have_mouse > 0)
+  if (have_mouse > 0 && !mouse_preempted)
     {
       int but, press, x, y, ok;
 
@@ -1956,7 +2019,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 ();
@@ -2077,7 +2140,7 @@ IT_menu_display (XMenu *menu, int y, int x, int *faces)
   text = (GLYPH *) xmalloc ((width + 2) * sizeof (GLYPH));
   ScreenGetCursor (&row, &col);
   mouse_get_xy (&mx, &my);
-  IT_update_begin ();
+  IT_update_begin (selected_frame);
   for (i = 0; i < menu->count; i++)
     {
       IT_cursor_to (y + i, x);
@@ -2104,7 +2167,7 @@ IT_menu_display (XMenu *menu, int y, int x, int *faces)
       *p++ = FAST_MAKE_GLYPH (menu->submenu[i] ? 16 : ' ', face);
       IT_write_glyphs (text, width + 2);
     }
-  IT_update_end ();
+  IT_update_end (selected_frame);
   IT_cursor_to (row, col);
   xfree (text);
 }
@@ -2234,6 +2297,10 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
   if (y0 <= 0)
     y0 = 1;
 
+  /* We will process all the mouse events directly, so we had
+     better prevented dos_rawgetc from stealing them from us.  */
+  mouse_preempted++;
+
   state = alloca (menu->panecount * sizeof (struct IT_menu_state));
   screensize = screen_size * 2;
   faces[0]
@@ -2356,11 +2423,22 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
                           state[statecount - 1].x,
                           faces);
        }
-      for (b = 0; b < mouse_button_count; b++)
+      else
+       /* 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 && !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);
        }
     }
 
@@ -2371,6 +2449,14 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
   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 ();
+  /* Allow mouse events generation by dos_rawgetc.  */
+  mouse_preempted--;
   return result;
 }
 
@@ -2771,6 +2857,38 @@ 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: ");
 
   /* Find our root from argv[0].  Assuming argv[0] is, say,
      "c:/emacs/bin/emacs.exe" our root will be "c:/emacs".  */
@@ -3391,6 +3509,25 @@ sigblock (mask) int mask; { return 0; }
        && (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
@@ -3424,9 +3561,7 @@ sys_select (nfds, rfds, wfds, efds, timeout)
     {
       while (!detect_input_pending ())
        {
-#if __DJGPP__ >= 2
-         __dpmi_yield ();
-#endif   
+         dos_yield_time_slice ();
        }
     }
   else
@@ -3452,9 +3587,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 ();
        }
     }