/* Epsilon mouse extensions */ /* The following copyright and trademark notice applies to some of the code * herein; all other material is Copyright (c) 1987 by Robert Lenoil, with * free copying allowed for any purpose, provided that this copyright notice * is included. */ /************************************************************************ * "Epsilon", "EEL" and "Lugaru" are trademarks of Lugaru Software, Ltd. * * * * Copyright (C) 1985 Lugaru Software Ltd. All rights reserved. * * * * Limited permission is hereby granted to reproduce and modify this * * copyrighted material provided that the resulting code is used only in * * conjunction with Lugaru products and that this notice is retained in * * any such reproduction or modification. * ************************************************************************/ /* This file adds the following mouse functions to Epsilon: * click left sets point at mouse cursor * double click left sets point at mark and mark at mouse cursor * drag left sets mark at start and point at finish * click right deletes from mark to mouse cursor * double click right yanks at mouse cursor * click left in echo area set point relative to position in echo area * (column 0 = top of buffer) * click right in echo area set mouse cursor column relative to point's * position in buffer * bump top/bottom edge scroll down/up */ #include "eel.h" #include "lowlevel.h" /* This file modifies the following Epsilon commands/procedures: COMMAND SOURCE FROM VERSION getkey 3.1 And adds the following commands/procedures: COMMAND WRITTEN FOR VERSION flush_mouse_buttons 3.1 hide_mouse_cursor 3.1 mouse 3.1 mouse_button_pressed 3.1 mouse_init 3.1 mouse_to_cursor 3.1 process_mouse_command 3.1 refresh_for_mouse 3.1 screen_set_point 3.1 set_mouse_cursor 3.1 show_mouse_cursor 3.1 start_up 3.1 And defines the following globals: GLOBAL WRITTEN FOR VERSION doubleclick_interval 3.1 mouse_present 3.1 */ int doubleclick_interval = 10; /* in ticks */ int mouse_present = 0; /* non-zero if a mouse is present in system */ /* START_UP calls mouse_init. */ /*when_loading() { mouse_init(); }*/ /* GETKEY modified to enable mouse for top-level input and process mouse * input. Modified from version 3.1 source. */ getkey() { int save_in_mac = len_def_mac && ungot_key == -1; /* Note: Must turn off mouse while a concurrent process is running, or * process won't get any cycles. (Fix this, Lugaru.) */ if (!cmd_len && !char_avail() && mouse_present && !another) { /* At top-level with no input pending; allow mouse input */ mouse_to_cursor(); flush_mouse_buttons(); show_mouse_cursor(); while (!char_avail()) if (mouse_button_pressed()) { prev_cmd = 0; process_mouse_command(); flush_mouse_buttons(); /* in case they hit other button too */ } hide_mouse_cursor(); } wait_for_key(); if (save_in_mac) { def_text[len_def_mac++] = key; if (len_def_mac >= MAX_MACRO) { end_kbd_macro(); error("Macro definition buffer full: keyboard macro defined"); } } return key; } /* REFRESH_FOR_MOUSE hides the mouse cursor, refreshes the screen, then * reenables the mouse cursor. */ refresh_for_mouse() { hide_mouse_cursor(); refresh(); show_mouse_cursor(); } #define MOUSE_SERVICES 0x33 /* Mouse interrupt */ #define LEFT_BUTTON 0 #define RIGHT_BUTTON 1 #define BMASK(button) (1<MOUSE.E. If you don't feel like editing the file, send me a note and I'll mail you the entire file. */ /* MOUSE_INIT performs mouse initialization, if a mouse is present. */ mouse_init() { m_regs.w.ax = 0; do_interrupt(MOUSE_SERVICES, &m_regs); /* initialize */ if (mouse_present = m_regs.w.ax) { /* Some mouse drivers don't know about EGA 43 row mode. Perform the EGA * information call to get the number of rows and manually set the * maximum vertical mouse position. */ m_regs.w.ax = 0x1130; /* EGA information call */ m_regs.b.bh = 0; m_regs.b.dl = 24; /* default to 25 in case no EGA in system */ do_interrupt(VIDEO_IO, &m_regs); m_regs.w.ax = 8; m_regs.w.cx = 0; m_regs.w.dx = (m_regs.b.dl + 1) * CWIDTH - 1; do_interrupt(MOUSE_SERVICES, &m_regs); } } #ifdef OLD_MOUSE /* MOUSE_INIT performs mouse initialization, if a mouse is present. */ mouse_init() { m_regs.w.ax = 0; do_interrupt(MOUSE_SERVICES, &m_regs); mouse_present = m_regs.w.ax; } #endif /* SHOW_MOUSE_CURSOR turns on the mouse cursor and clears the mickey count. */ show_mouse_cursor() { m_regs.w.ax = 1; do_interrupt(MOUSE_SERVICES, &m_regs); m_regs.w.ax = 11; do_interrupt(MOUSE_SERVICES, &m_regs); } hide_mouse_cursor() { m_regs.w.ax = 2; do_interrupt(MOUSE_SERVICES, &m_regs); } /* FLUSH_MOUSE_BUTTONS resets the stored number of button presses */ flush_mouse_buttons() { short button; for (button = LEFT_BUTTON; button <= RIGHT_BUTTON; ++button) { m_regs.w.ax = 5; m_regs.w.bx = button; do_interrupt(MOUSE_SERVICES, &m_regs); } } /* MOUSE_BUTTON_PRESSED returns the status of the mouse buttons (button is * down if status & BMASK(button) non-zero). It also handles vertical screen * scrolling if the mouse if bumping against the top or bottom of the screen. */ mouse_button_pressed() { short status, row; for (;;) { m_regs.w.ax = 3; do_interrupt(MOUSE_SERVICES, &m_regs); status = m_regs.w.bx & (BMASK(LEFT_BUTTON) + BMASK(RIGHT_BUTTON)); row = m_regs.w.dx / CWIDTH; m_regs.w.ax = 11; do_interrupt(MOUSE_SERVICES, &m_regs); /* If the mouse is at the top (or bottom) line and is still moving up * (or down) and there is not much horizontal motion, then scroll the * screen. */ if ( !(m_regs.w.cx / 8) && ((row == 0 && m_regs.w.dx < 0) || (row == screen_lines - 1 && m_regs.w.dx > 0)) ) if (prev_cmd == SCROLL) { window_scroll(m_regs.w.dx / 4); refresh_for_mouse(); } else { pause(3); /* just hit edge, punt any overkill */ prev_cmd = SCROLL; m_regs.w.ax = 11; do_interrupt(MOUSE_SERVICES, &m_regs); } else { if (prev_cmd == SCROLL && row > 0 && row < screen_lines - 1) prev_cmd = 0; break; } } return status; } process_mouse_command() { short status; pause(doubleclick_interval); /* give user time to double click */ m_regs.w.ax = 5; /* Get button press information */ m_regs.w.bx = LEFT_BUTTON; do_interrupt(MOUSE_SERVICES, &m_regs); status = m_regs.w.ax; switch (m_regs.w.bx) { case 1: /* Check if user clicked in echo area */ if ((m_regs.w.dx / CWIDTH) == screen_lines - 1) { /* Position window over section of buffer relative to where * the user clicked in the echo area, with the left-hand * side corresponding to the top of the buffer. */ point = size() * m_regs.w.cx / (screen_cols - 1) / CWIDTH; build_first = 1; break; } /* Single-click left - set point at mouse cursor */ screen_set_point(m_regs.w.dx, m_regs.w.cx); refresh_for_mouse(); if (status & BMASK(LEFT_BUTTON)) { /* The button is still being held down. Set mark at point, then * set point at new mouse cursor when button is released. */ set_mark(); while (mouse_button_pressed() & BMASK(LEFT_BUTTON)); m_regs.w.ax = 6; m_regs.w.bx = LEFT_BUTTON; do_interrupt(MOUSE_SERVICES, &m_regs); screen_set_point(m_regs.w.dx, m_regs.w.cx); } break; case 2: /* Double-click left - set mark at mouse cursor, then set point and * mouse cursor to previous mark. */ screen_set_point(m_regs.w.dx, m_regs.w.cx); exchange_point_and_mark(); refresh_for_mouse(); mouse_to_cursor(); return; case 0: /* No left clicks, try the right button */ m_regs.w.ax = 5; /* Get button press information */ m_regs.w.bx = RIGHT_BUTTON; do_interrupt(MOUSE_SERVICES, &m_regs); switch (m_regs.w.bx) { int *point_save, pos; case 0: return; case 1: /* Check if user clicked in echo area */ if ((m_regs.w.dx / CWIDTH) == screen_lines - 1) { /* Yes, set column to indicate percentage of file */ set_mouse_cursor( m_regs.w.dx, point * CWIDTH * (screen_cols - 1) / size() ); break; } /* else fall through to case 2 */ /* Click right - delete from mark to mouse cursor */ case 2: /* Double click right - yank at mouse cursor */ point_save = alloc_spot(); if (screen_set_point(m_regs.w.dx, m_regs.w.cx)) { if (m_regs.w.bx == 1) kill_region(); else yank(); refresh_for_mouse(); mouse_to_cursor(); point = *point_save; } else maybe_ding(); free_spot(point_save); break; default: goto too_many_clicks; } break; default: too_many_clicks: maybe_ding(); return; } hide_mouse_cursor(); maybe_refresh(); show_mouse_cursor(); } /* SCREEN_SET_POINT takes mouse row and column coordinates and sets point * there. If the coordinates lie in another window, it is made current. * Non-zero is returned if the specified row is an editable line, otherwise * (modeline or echo area) zero is returned. */ screen_set_point(row, col) { int r, current_window = window_number; /* change coordinates from pixels to characters */ row /= CWIDTH; col /= CWIDTH; /* Find which window the coordinates lie in */ for (window_number = r = 0; r < screen_lines - 1; ++window_number) if (r + window_size + 1 > row) { r = row - r; if (r < window_size) /* if row = mode line, don't change point */ { point = window_start; point = next_screen_line(r); r = current_column(); move_to_column(r + col); /* Note: Bug in Epsilon 3.1 - move_to_column() moves one character * too far on continuation lines. Reported to Lugaru 5/20/87. */ r = current_column() - (r + col); if (r > 0) point -= r; return 1; } else return 0; } else r += window_size + 1; window_number = current_window; /* row = echo area, don't do anything */ return 0; } /* MOUSE_TO_CURSOR warps the mouse cursor to the hardware cursor */ mouse_to_cursor() { m_regs.b.ah = 3; /* read cursor position */ m_regs.b.bh = 0; do_interrupt(VIDEO_IO, &m_regs); set_mouse_cursor(m_regs.b.dh * CWIDTH, m_regs.b.dl * CWIDTH); } /* SET_MOUSE_CURSOR sets the mouse cursor to the given row and column */ set_mouse_cursor(row, col) { m_regs.w.ax = 4; m_regs.w.cx = col; m_regs.w.dx = row; do_interrupt(MOUSE_SERVICES, &m_regs); } /* BIOS_TICKS returns the number of bios ticks */ int bios_ticks() { int ticks ; m_regs.b.ah = 0; do_interrupt(0x1a, &m_regs); if (m_regs.b.al == 0) { /* calculate ticks */ ticks = (m_regs.w.cx << 16) + m_regs.w.dx ; } else { /* we passed midnight since last call, make sure pause() exits */ ticks = 0x7fffffffL ; } return ticks ; } /* SET_DOUBLECLICK allows you set the double click wait interval */ command set_doubleclick() { char prompt[80] ; sprintf(prompt, "Current double click interval is %d. New Value: ", doubleclick_interval) ; doubleclick_interval = get_number(prompt) ; } /* PAUSE waits for the specified number of BIOS ticks */ pause(ticks) int ticks ; { int alarm ; alarm = bios_ticks() + ticks ; while (alarm > bios_ticks()) { } }