DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/serialport/serialmouse.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 /* Microsoft Serial Mouse compatible emulation.
00020  * Written by Jonathan Campbell */
00021 
00022 #include "dosbox.h"
00023 
00024 #include "mouse.h"
00025 #include "setup.h"
00026 #include "serialdummy.h"
00027 #include "serialmouse.h"
00028 #include "serialport.h"
00029 
00030 static CSerialMouse *serial_mice[4] = {NULL};
00031 
00032 /* this function is the method the GUI and mouse emulation notifies us of movement/button events.
00033  * if the serial mouse is not installed, it is ignored */
00034 void on_mouse_event_for_serial(int delta_x,int delta_y,Bit8u buttonstate) {
00035         int i;
00036 
00037         for (i=0;i < 4;i++) {
00038                 if (serial_mice[i] != NULL)
00039                         serial_mice[i]->on_mouse_event(delta_x,delta_y,buttonstate);
00040         }
00041 }
00042 
00043 void CSerialMouse::start_packet() {
00044         xmit_another_packet = false;
00045 
00046         /* FIX: Default mapping deltas as-is in previous versions of this code
00047          *      resulted in serial mouse input that was way too sensitive */
00048         mouse_delta_x /= 4;
00049         mouse_delta_y /= 4;
00050 
00051         if (mouse_delta_x < -128) mouse_delta_x = -128;
00052         else if (mouse_delta_x > 127) mouse_delta_x = 127;
00053         mouse_delta_x &= 0xFF;
00054 
00055         if (mouse_delta_y < -128) mouse_delta_y = -128;
00056         else if (mouse_delta_y > 127) mouse_delta_y = 127;
00057         mouse_delta_y &= 0xFF;
00058 
00059         /* NOTE TO SELF: Do NOT set bit 7. It confuses CTMOUSE.EXE (CuteMouse) serial support.
00060          *               Leaving it clear is the only way to make mouse movement possible.
00061          *               Microsoft Windows on the other hand doesn't care if bit 7 is set. */
00062         packet_xmit = 0;
00063         /* Byte 0:    X   1  LB  RB  Y7  Y6  X7  X6 */
00064         packet[0] = 0x40 | (mouse_buttons << 4) | (((mouse_delta_y >> 6) & 3) << 2) | ((mouse_delta_x >> 6) & 3);
00065         /* Byte 1:    X   0  X5-X0 */
00066         packet[1] = 0x00 | (mouse_delta_x & 0x3F);
00067         /* Byte 2:    X   0  Y5-Y0 */
00068         packet[2] = 0x00 | (mouse_delta_y & 0x3F);
00069 
00070         /* clear counters */
00071         mouse_delta_x = mouse_delta_y = 0;
00072 
00073         setEvent(SERIAL_RX_EVENT, bytetime);
00074 }
00075 
00076 void CSerialMouse::on_mouse_event(int delta_x,int delta_y,Bit8u buttonstate) {
00077         mouse_buttons = ((buttonstate & 1) ? 2 : 0) | ((buttonstate & 2) ? 1 : 0);
00078         mouse_delta_x += delta_x;
00079         mouse_delta_y += delta_y;
00080 
00081         /* initiate data transfer and form the packet to transmit. if another packet
00082          * is already transmitting now then wait for it to finish before transmitting ours,
00083          * and let the mouse motion accumulate in the meantime */
00084         if (packet_xmit >= 3) start_packet();
00085         else xmit_another_packet = true;
00086 }
00087 
00088 CSerialMouse::CSerialMouse(Bitu id,     CommandLine* cmd):CSerial(id, cmd) {
00089         CSerial::Init_Registers();
00090         setRI(false);
00091         setDSR(false);
00092         setCD(false);
00093         setCTS(false);
00094         InstallationSuccessful=true;
00095         send_ack=true;
00096         packet_xmit=0xFF;
00097         mouse_buttons=0;
00098         serial_mice[id] = this; /* <- NTS: 'id' is just the index of the serial mouse 0..3 for COM1...COM4 */
00099         xmit_another_packet=false;
00100         mouse_delta_x=mouse_delta_y=0;
00101 }
00102 
00103 CSerialMouse::~CSerialMouse() {
00104         // clear events
00105         removeEvent(SERIAL_TX_EVENT);
00106 }
00107 
00108 void CSerialMouse::handleUpperEvent(Bit16u type) {
00109         if(type==SERIAL_TX_EVENT) {
00110         //LOG_MSG("SERIAL_TX_EVENT");
00111                 ByteTransmitted(); // tx timeout
00112         }
00113         else if(type==SERIAL_THR_EVENT){
00114                 //LOG_MSG("SERIAL_THR_EVENT");
00115                 ByteTransmitting();
00116                 setEvent(SERIAL_TX_EVENT,bytetime);
00117         }
00118         else if (type==SERIAL_RX_EVENT) {
00119                 // check for bytes to be sent to port
00120                 if(CSerial::CanReceiveByte()) {
00121                         if (send_ack) {
00122                                 send_ack = 0;
00123                                 CSerial::receiveByte('M');
00124                                 setEvent(SERIAL_RX_EVENT, bytetime);
00125                         }
00126                         else if (packet_xmit < 3) {
00127                                 CSerial::receiveByte(packet[packet_xmit++]);
00128                                 if (packet_xmit >= 3 && xmit_another_packet)
00129                                         start_packet();
00130                                 else
00131                                         setEvent(SERIAL_RX_EVENT, bytetime);
00132                         }
00133                 }
00134                 else {
00135                         setEvent(SERIAL_RX_EVENT, bytetime);
00136                 }
00137         }
00138 }
00139 
00140 /*****************************************************************************/
00141 /* updatePortConfig is called when emulated app changes the serial port     **/
00142 /* parameters baudrate, stopbits, number of databits, parity.               **/
00143 /*****************************************************************************/
00144 void CSerialMouse::updatePortConfig(Bit16u divider, Bit8u lcr) {
00145     (void)divider;//UNUSED
00146     (void)lcr;//UNUSED
00147         //LOG_MSG("Serial port at 0x%x: Port params changed: %d Baud", base,dcb.BaudRate);
00148 }
00149 
00150 void CSerialMouse::updateMSR() {
00151 }
00152 void CSerialMouse::transmitByte(Bit8u val, bool first) {
00153     (void)val;//UNUSED
00154         if(first) setEvent(SERIAL_THR_EVENT, bytetime/10); 
00155         else setEvent(SERIAL_TX_EVENT, bytetime);
00156 }
00157 
00158 /*****************************************************************************/
00159 /* setBreak(val) switches break on or off                                   **/
00160 /*****************************************************************************/
00161 
00162 void CSerialMouse::setBreak(bool value) {
00163     (void)value;//UNUSED
00164         //LOG_MSG("UART 0x%x: Break toggeled: %d", base, value);
00165 }
00166 
00167 void CSerialMouse::onMouseReset() {
00168         send_ack = 1;
00169         packet_xmit=0xFF;
00170         mouse_buttons=0;
00171         xmit_another_packet=false;
00172         mouse_delta_x=mouse_delta_y=0;
00173         setEvent(SERIAL_RX_EVENT, bytetime);
00174         Mouse_AutoLock(true);
00175 }
00176 
00177 /*****************************************************************************/
00178 /* setRTSDTR sets the modem control lines                                   **/
00179 /*****************************************************************************/
00180 void CSerialMouse::setRTSDTR(bool rts, bool dtr) {
00181         if (rts && dtr && !getRTS() && !getDTR()) {
00182                 /* The serial mouse driver turns on the mouse by bringing up
00183                  * RTS and DTR. Not just for show, but to give the serial mouse
00184                  * a power source to work from. Likewise, drivers "reset" the
00185                  * mouse by bringing down the lines, then bringing them back
00186                  * up. And most drivers turn off the mouse when not in use by
00187                  * bringing them back down and leaving them that way.
00188                  *
00189                  * We're expected to transmit ASCII character 'M' when first
00190                  * initialized, so that the driver knows we're a Microsoft
00191                  * compatible serial mouse attached to a COM port. */
00192                 onMouseReset();
00193         }
00194 
00195         setRTS(rts);
00196         setDTR(dtr);
00197 }
00198 void CSerialMouse::setRTS(bool val) {
00199         if (val && !getRTS() && getDTR()) {
00200                 onMouseReset();
00201         }
00202 
00203         setCTS(val);
00204 }
00205 void CSerialMouse::setDTR(bool val) {
00206         if (val && !getDTR() && getRTS()) {
00207                 onMouseReset();
00208         }
00209 
00210         setDSR(val);
00211         setRI(val);
00212         setCD(val);
00213 }