📄 os_msdos.c
字号:
/* vi:set ts=8 sts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* os_msdos.c
*
* MSDOS system-dependent routines.
* A cheap plastic imitation of the amiga dependent code.
* A lot in this file was made by Juergen Weigert (jw).
*
* DJGPP changes by Gert van Antwerpen
* Faster text screens by John Lange (jlange@zilker.net)
*
*/
#include <io.h>
#include "vim.h"
#include <conio.h>
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#include <bios.h>
#ifdef DJGPP
# include <dpmi.h>
# include <signal.h>
# include <sys/movedata.h>
#else
# include <alloc.h>
#endif
#if defined(DJGPP) || defined(PROTO)
# define _cdecl /* DJGPP doesn't have this */
#endif
static int cbrk_pressed = FALSE; /* set by ctrl-break interrupt */
static int ctrlc_pressed = FALSE; /* set when ctrl-C or ctrl-break detected */
static int delayed_redraw = FALSE; /* set when ctrl-C detected */
#ifdef USE_MOUSE
static int mouse_avail = FALSE; /* mouse present */
static int mouse_active; /* mouse enabled */
static int mouse_hidden; /* mouse not shown */
static int mouse_click = -1; /* mouse status */
static int mouse_last_click = -1; /* previous status at click */
static int mouse_x = -1; /* mouse x coodinate */
static int mouse_y = -1; /* mouse y coodinate */
static long mouse_click_time = 0; /* biostime() of last click */
static int mouse_click_count = 0; /* count for multi-clicks */
static int mouse_click_x = 0; /* x of previous mouse click */
static int mouse_click_y = 0; /* y of previous mouse click */
static linenr_t mouse_topline = 0; /* topline at previous mouse click */
static int mouse_x_div = 8; /* column = x coord / mouse_x_div */
static int mouse_y_div = 8; /* line = y coord / mouse_y_div */
#endif
#define BIOSTICK 55 /* biostime() increases one tick about
every 55 msec */
#ifdef DJGPP
/*
* For DJGPP, use our own functions for fast text screens. JML 1/18/98
*/
unsigned long S_ulScreenBase = 0xb8000;
unsigned short S_uiAttribute = 7 << 8;
int S_iCurrentRow = 0; /* These are 0 offset */
int S_iCurrentColumn = 0;
int S_iLeft = 0; /* These are 1 offset */
int S_iTop = 0;
int S_iRight = 0;
int S_iBottom = 0;
short S_selVideo; /* Selector for DJGPP direct video transfers */
static void
mygotoxy(int x, int y)
{
S_iCurrentRow = y - 1;
S_iCurrentColumn = x - 1;
gotoxy(x,y); /* set cursor position */
}
static void
myscroll(void)
{
short iRow, iColumn;
unsigned short uiValue;
/* Copy the screen */
for (iRow = S_iTop; iRow < S_iBottom; iRow++)
movedata(S_selVideo, (iRow * Columns) << 1,
S_selVideo, ((iRow - 1) * Columns) << 1,
(S_iRight - S_iLeft + 1) << 1);
/* Clear the bottom row */
uiValue = S_uiAttribute | ' ';
for (iColumn = S_iLeft - 1; iColumn < S_iRight; iColumn++)
_dosmemputw(&uiValue, 1, S_ulScreenBase
+ (S_iBottom - 1) * (Columns << 1) + (iColumn << 1));
}
static int
myputch(int iChar)
{
unsigned int *puiLocation;
unsigned short uiValue;
if (iChar == '\n')
{
if (S_iCurrentRow >= S_iBottom - S_iTop)
myscroll();
else
mygotoxy(S_iLeft, S_iCurrentRow + 2);
}
else if (iChar == '\r')
mygotoxy(S_iLeft, S_iCurrentRow + 1);
else if (iChar == '\b')
mygotoxy(S_iCurrentColumn, S_iCurrentRow + 1);
else if (iChar == 7)
{
sound(440); /* short beep */
delay(200);
nosound();
}
else
{
uiValue = S_uiAttribute | (unsigned char)iChar;
_dosmemputw(&uiValue, 1, S_ulScreenBase
+ S_iCurrentRow * (Columns << 1) + (S_iCurrentColumn << 1));
S_iCurrentColumn++;
if (S_iCurrentColumn >= S_iRight && S_iCurrentRow >= S_iBottom - S_iTop)
{
myscroll();
mygotoxy(S_iLeft, S_iCurrentRow + 2);
}
else
mygotoxy(S_iCurrentColumn + 1, S_iCurrentRow + 1);
}
return 0;
}
static void
mywindow(int iLeft, int iTop, int iRight, int iBottom)
{
S_iLeft = iLeft;
S_iTop = iTop;
S_iRight = iRight;
S_iBottom = iBottom;
window(iLeft, iTop, iRight, iBottom);
}
static void
mytextinit(struct text_info *pTextinfo)
{
S_selVideo = __dpmi_segment_to_descriptor(S_ulScreenBase >> 4);
S_uiAttribute = pTextinfo->normattr << 8;
mywindow(1, 1, Columns, Rows);
}
static void
get_screenbase(void)
{
static union REGS regs;
/* old Hercules grafic card has different base address (Macewicz) */
regs.h.ah = 0x0f;
(void)int86(0x10, ®s, ®s); /* int 10 0f */
if (regs.h.al == 0x07) /* video mode 7 -- hercules mono */
S_ulScreenBase = 0xb0000;
else
S_ulScreenBase = 0xb8000;
}
static void
mynormvideo(void)
{
S_uiAttribute = 0x700;
textbackground(0); /*for delline() etc*/
}
static void
mytextattr(int iAttribute)
{
S_uiAttribute = (unsigned short)iAttribute << 8;
iAttribute >>= 4;
if (iAttribute < 8)
textbackground(iAttribute); /*for delline() etc*/
else
textbackground(0);
}
static void
mytextcolor(int iTextColor)
{
S_uiAttribute = (unsigned short)((S_uiAttribute & 0xf000)
| (unsigned short)iTextColor << 8);
textcolor(iTextColor); /*for delline() etc*/
}
static void
mytextbackground(int iBkgColor)
{
S_uiAttribute = (unsigned short)((S_uiAttribute & 0x0f00)
| (unsigned short)(iBkgColor << 12));
if (iBkgColor < 8)
textbackground(iBkgColor); /*for delline() etc*/
else
textbackground(0);
}
#else
# define mygotoxy gotoxy
# define myputch putch
# define myscroll scroll
# define mywindow window
# define mynormvideo normvideo
# define mytextattr textattr
# define mytextcolor textcolor
# define mytextbackground textbackground
#endif
/*
* Save/restore the shape of the cursor.
* call with FALSE to save, TRUE to restore
*/
static void
mch_restore_cursor_shape(int restore)
{
static union REGS regs;
static int saved = FALSE;
if (restore)
{
if (saved)
regs.h.ah = 0x01; /*Set Cursor*/
else
return;
}
else
{
regs.h.ah = 0x03; /*Get Cursor*/
regs.h.bh = 0x00; /*Page */
saved = TRUE;
}
(void)int86(0x10, ®s, ®s);
}
/*
* Set the shape of the cursor.
* 'thickness' can be from 0 (thin) to 7 (block)
*/
static void
mch_set_cursor_shape(int thickness)
{
union REGS regs;
regs.h.ch = 7 - thickness; /*Starting Line*/
regs.h.cl = 7; /*Ending Line*/
regs.h.ah = 0x01; /*Set Cursor*/
(void)int86(0x10, ®s, ®s);
}
void
mch_update_cursor(void)
{
int idx;
int thickness;
/*
* How the cursor is drawn depends on the current mode.
*/
idx = get_cursor_idx();
if (cursor_table[idx].shape == SHAPE_BLOCK)
thickness = 7;
else
thickness = (7 * cursor_table[idx].percentage + 90) / 100;
mch_set_cursor_shape(thickness);
}
long_u
mch_avail_mem(int special)
{
#ifdef DJGPP
return _go32_dpmi_remaining_virtual_memory();
#else
return coreleft();
#endif
}
#ifdef USE_MOUSE
/*
* Set area where mouse can be moved to: The whole screen.
* Rows must be valid when calling!
*/
static void
mouse_area(void)
{
union REGS regs;
int mouse_y_max; /* maximum mouse y coord */
if (mouse_avail)
{
mouse_y_max = Rows * mouse_y_div - 1;
if (mouse_y_max < 199) /* is this needed? */
mouse_y_max = 199;
regs.x.cx = 0; /* mouse visible between cx and dx */
regs.x.dx = 639;
regs.x.ax = 7;
(void)int86(0x33, ®s, ®s);
regs.x.cx = 0; /* mouse visible between cx and dx */
regs.x.dx = mouse_y_max;
regs.x.ax = 8;
(void)int86(0x33, ®s, ®s);
}
}
static void
show_mouse(int on)
{
static int was_on = FALSE;
union REGS regs;
if (mouse_avail)
{
if (!mouse_active || mouse_hidden)
on = FALSE;
/*
* Careful: Each switch on must be compensated by exactly one switch
* off
*/
if (on && !was_on || !on && was_on)
{
was_on = on;
regs.x.ax = on ? 1 : 2;
int86(0x33, ®s, ®s); /* show mouse */
if (on)
mouse_area();
}
}
}
#endif
#ifdef DJGPP
/*
* DJGPP provides a kbhit() function that goes to the BIOS instead of DOS.
* This doesn't work for terminals connected to a serial port.
* Redefine kbhit() here to make it work.
*/
static int
vim_kbhit(void)
{
union REGS regs;
regs.h.ah = 0x0b;
(void)intdos(®s, ®s);
return regs.h.al;
}
#ifdef kbhit
# undef kbhit /* might have been defined in conio.h */
#endif
#define kbhit() vim_kbhit()
#endif
/*
* Simulate WaitForChar() by slowly polling with bioskey(1) or kbhit().
*
* If Vim should work over the serial line after a 'ctty com1' we must use
* kbhit() and getch(). (jw)
* Usually kbhit() is not used, because then CTRL-C and CTRL-P
* will be catched by DOS (mool).
*
* return TRUE if a character is available, FALSE otherwise
*/
#define FOREVER 1999999999L
static int
WaitForChar(long msec)
{
union REGS regs;
long starttime;
int x, y;
starttime = biostime(0, 0L);
for (;;)
{
#ifdef USE_MOUSE
long clicktime;
static int last_status = 0;
if (mouse_avail && mouse_active && mouse_click < 0)
{
regs.x.ax = 3;
int86(0x33, ®s, ®s); /* check mouse status */
/* only recognize button-down and button-up event */
x = regs.x.cx / mouse_x_div;
y = regs.x.dx / mouse_y_div;
if ((last_status == 0) != (regs.x.bx == 0))
{
if (last_status) /* button up */
mouse_click = MOUSE_RELEASE;
else /* button down */
{
/*
* Translate MSDOS mouse events to Vim mouse events.
* TODO: should handle middle mouse button, by pressing
* left and right at the same time.
*/
if (regs.x.bx & MSDOS_MOUSE_LEFT)
mouse_click = MOUSE_LEFT;
else if (regs.x.bx & MSDOS_MOUSE_RIGHT)
mouse_click = MOUSE_RIGHT;
else if (regs.x.bx & MSDOS_MOUSE_MIDDLE)
mouse_click = MOUSE_MIDDLE;
/*
* Find out if this is a multi-click
*/
clicktime = biostime(0, 0L);
if (mouse_click_x == x && mouse_click_y == y &&
mouse_topline == curwin->w_topline &&
mouse_click_count != 4 &&
mouse_click == mouse_last_click &&
clicktime < mouse_click_time + p_mouset / BIOSTICK)
++mouse_click_count;
else
mouse_click_count = 1;
mouse_click_time = clicktime;
mouse_last_click = mouse_click;
mouse_click_x = x;
mouse_click_y = y;
mouse_topline = curwin->w_topline;
SET_NUM_MOUSE_CLICKS(mouse_click, mouse_click_count);
}
}
else if (last_status && (x != mouse_x || y != mouse_y))
mouse_click = MOUSE_DRAG;
last_status = regs.x.bx;
if (mouse_hidden && mouse_x >= 0 && (mouse_x != x || mouse_y != y))
{
mouse_hidden = FALSE;
show_mouse(TRUE);
}
mouse_x = x;
mouse_y = y;
}
#endif
if ((p_biosk ? bioskey(1) : kbhit()) || cbrk_pressed
#ifdef USE_MOUSE
|| mouse_click >= 0
#endif
)
return TRUE;
/*
* Use biostime() to wait until our time is done.
* We busy-wait here. Unfortunately, delay() and usleep() have been
* reported to give problems with the original Windows 95. This is
* fixed in service pack 1, but not everybody installed that.
*/
if (msec != FOREVER && biostime(0, 0L) > starttime + msec / BIOSTICK)
break;
}
return FALSE;
}
/*
* don't do anything for about "msec" msec
*/
void
mch_delay(
long msec,
int ignoreinput)
{
long starttime;
if (ignoreinput)
{
/*
* We busy-wait here. Unfortunately, delay() and usleep() have been
* reported to give problems with the original Windows 95. This is
* fixed in service pack 1, but not everybody installed that.
*/
starttime = biostime(0, 0L);
while (biostime(0, 0L) < starttime + msec / BIOSTICK)
;
}
else
WaitForChar(msec);
}
/*
* this version of remove is not scared by a readonly (backup) file
*
* returns -1 on error, 0 otherwise (just like remove())
*/
int
mch_remove(char_u *name)
{
(void)mch_setperm(name, 0); /* default permissions */
return unlink((char *)name);
}
/*
* mch_write(): write the output buffer to the screen
*/
void
mch_write(
char_u *s,
int len)
{
char_u *p;
int row, col;
if (term_console) /* translate ESC | sequences into bios calls */
while (len--)
{
if (p_wd) /* testing: wait a bit for each char */
WaitForChar(p_wd);
if (s[0] == '\n')
myputch('\r');
else if (s[0] == ESC && len > 1 && s[1] == '|')
{
switch (s[2])
{
#ifdef DJGPP
case 'B': ScreenVisualBell();
goto got3;
#endif
case 'J': clrscr();
goto got3;
case 'K': clreol();
goto got3;
case 'L': insline();
goto got3;
case 'M': delline();
got3: s += 3;
len -= 2;
continue;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': p = s + 2;
row = getdigits(&p); /* no check for length! */
if (p > s + len)
break;
if (*p == ';')
{
++p;
col = getdigits(&p); /* no check for length! */
if (p > s + len)
break;
if (*p == 'H' || *p == 'r')
{
if (*p == 'H') /* set cursor position */
mygotoxy(col, row);
else /* set scroll region */
mywindow(1, row, Columns, col);
len -= p - s;
s = p + 1;
continue;
}
}
else if (*p == 'm' || *p == 'f' || *p == 'b')
{
if (*p == 'm') /* set color */
{
if (row == 0)
mynormvideo();/* reset color */
else
mytextattr(row);
}
else if (*p == 'f') /* set foreground color */
mytextcolor(row);
else /* set background color */
mytextbackground(row);
len -= p - s;
s = p + 1;
continue;
}
}
}
myputch(*s++);
}
else
write(1, s, (unsigned)len);
}
/*
* mch_inchar(): low level input funcion.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -