VirtualBox

Changeset 80039 in vbox for trunk/src/VBox/Devices/Input


Ignore:
Timestamp:
Jul 29, 2019 10:31:03 AM (6 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
132475
Message:

PS2M: Implemented (again) support for horizontal scrolling over the PS/2 protocol (see bugref:4118). RIP MJT

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Input/PS2M.cpp

    r76553 r80039  
    5252 * commands with arguments 200, 200, 80 switch to ImEx mode. The Read ID (0F2h)
    5353 * command will report the currently selected protocol.
     54 *
     55 * There is an extended ImEx mode with support for horizontal scrolling. It is
     56 * entered from ImEx mode with a 200, 80, 40 sequence of Set Sampling Rate
     57 * commands. It does not change the reported protocol (it remains 4, or ImEx)
     58 * but changes the meaning of the 4th byte.
    5459 *
    5560 *
     
    9095 * | Byte 4 |   0    |   0    | Btn 5  | Btn 4  |  Z mov't delta (two's complement) |
    9196 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
    92  *
     97 *
     98 *   - The Z delta values are in practice only -1/+1; some mice (A4tech?) report
     99 *     horizontal scrolling as -2/+2.
     100 *
     101 * IntelliMouse Explorer (ImEx) fourth report packet byte when scrolling:
     102 *
     103 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
     104 * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
     105 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
     106 * | Byte 4 |   V    |   H    |      Z or W movement delta (two's complement)       |
     107 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
     108 *
     109 *   - Buttons 4 and 5 are reported as with the regular ImEx protocol, but not when
     110 *     scrolling. This is a departure from the usual logic because when the mouse
     111 *     sends scroll events, the state of buttons 4/5 is not reported and the last
     112 *     reported state should be assumed.
     113 *
     114 *   - When the V bit (bit 7) is set, vertical scroll (Z axis) is being reported.
     115 *     When the H bit (bit 6) is set, horizontal scroll (W axis) is being reported.
     116 *     The H and V bits are never set at the same time (also see below). When
     117 *     the H and V bits are both clear, button 4/5 state is being reported.
     118 *
     119 *   - The Z/W delta is extended to 6 bits. Z (vertical) values are not restricted
     120 *     to -1/+1, although W (horizontal) values are. Z values of at least -20/+20
     121 *     can be seen in practice.
     122 *
     123 *   - Horizontal and vertical scroll is mutually exclusive. When the button is
     124 *     tilted, no vertical scrolling is reported, i.e. horizontal scrolling
     125 *     has priority over vertical.
     126 *
     127 *   - Positive values indicate down/right direction, negative values up/left.
     128 *
     129 *   - When the scroll button is tilted to engage horizontal scrolling, the mouse
     130 *     keeps sending events at a rate of 4 or 5 per second as long as the button
     131 *     is tilted.
     132 *
     133 * All report formats were verified with a real Microsoft IntelliMouse Explorer 4.0
     134 * mouse attached through a PS/2 port.
     135 *
     136 * The button "accumulator" is necessary to avoid missing brief button presses.
     137 * Without it, a very fast mouse button press + release might be lost if it
     138 * happened between sending reports. The accumulator latches button presses to
     139 * prevent that.
     140 *
    93141 */
    94142
     
    197245/* Protocols supported by the PS/2 mouse. */
    198246typedef enum {
    199     PS2M_PROTO_PS2STD = 0,  /* Standard PS/2 mouse protocol. */
    200     PS2M_PROTO_IMPS2  = 3,  /* IntelliMouse PS/2 protocol. */
    201     PS2M_PROTO_IMEX   = 4   /* IntelliMouse Explorer protocol. */
     247    PS2M_PROTO_PS2STD      = 0,  /* Standard PS/2 mouse protocol. */
     248    PS2M_PROTO_IMPS2       = 3,  /* IntelliMouse PS/2 protocol. */
     249    PS2M_PROTO_IMEX        = 4,  /* IntelliMouse Explorer protocol. */
     250    PS2M_PROTO_IMEX_HORZ   = 5   /* IntelliMouse Explorer with horizontal reports. */
    202251} PS2M_PROTO;
    203252
     
    207256    PS2M_KNOCK_1ST,
    208257    PS2M_KNOCK_IMPS2_2ND,
    209     PS2M_KNOCK_IMEX_2ND
     258    PS2M_KNOCK_IMEX_2ND,
     259    PS2M_KNOCK_IMEX_HORZ_2ND
    210260} PS2M_KNOCK_STATE;
    211261
     
    243293    /** Accumulated vertical movement. */
    244294    int32_t             iAccumY;
    245     /** Accumulated Z axis movement. */
     295    /** Accumulated Z axis (vertical scroll) movement. */
    246296    int32_t             iAccumZ;
     297    /** Accumulated W axis (horizontal scroll) movement. */
     298    int32_t             iAccumW;
    247299    /** Accumulated button presses. */
    248300    uint32_t            fAccumB;
     
    476528    /* Event queue, eccumulators, and button status bits are cleared. */
    477529    ps2kClearQueue((GeneriQ *)&pThis->evtQ);
    478     pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = pThis->fAccumB;
     530    pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = pThis->iAccumW = pThis->fAccumB = 0;
    479531}
    480532
     
    493545        else if (rate == 200)
    494546            pThis->enmKnockState = PS2M_KNOCK_IMEX_2ND;
     547        else if (rate == 80)
     548            pThis->enmKnockState = PS2M_KNOCK_IMEX_HORZ_2ND;
    495549        else
    496550            pThis->enmKnockState = PS2M_KNOCK_INITIAL;
     
    510564            LogRelFlow(("PS2M: Switching mouse to ImEx protocol.\n"));
    511565        }
     566        pThis->enmKnockState = PS2M_KNOCK_INITIAL;
     567        break;
     568    case PS2M_KNOCK_IMEX_HORZ_2ND:
     569        if (rate == 40)
     570        {
     571            pThis->enmProtocol = PS2M_PROTO_IMEX_HORZ;
     572            LogRelFlow(("PS2M: Switching mouse ImEx with horizontal scrolling.\n"));
     573        }
    512574        RT_FALL_THRU();
    513575    default:
     
    518580/* Three-button event mask. */
    519581#define PS2M_STD_BTN_MASK   (RT_BIT(0) | RT_BIT(1) | RT_BIT(2))
     582/* ImEx button 4/5 event mask. */
     583#define PS2M_IMEX_BTN_MASK  (RT_BIT(3) | RT_BIT(4))
    520584
    521585/* Report accumulated movement and button presses, then clear the accumulators. */
     
    524588    uint32_t    fBtnState = fAccumBtns ? pThis->fAccumB : pThis->fCurrB;
    525589    uint8_t     val;
    526     int         dX, dY, dZ;
     590    int         dX, dY, dZ, dW;
    527591
    528592    /* Clamp the accumulated delta values to the allowed range. */
    529593    dX = RT_MIN(RT_MAX(pThis->iAccumX, -255), 255);
    530594    dY = RT_MIN(RT_MAX(pThis->iAccumY, -255), 255);
    531     dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -8), 7);
    532595
    533596    /* Start with the sync bit and buttons 1-3. */
     
    544607    ps2kInsertQueue(pQueue, dY);
    545608
    546     /* Add fourth byte if extended protocol is in use. */
     609    /* Add fourth byte if an extended protocol is in use. */
    547610    if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
    548611    {
     612        /* Start out with 4-bit dZ range. */
     613        dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -8), 7);
     614
    549615        if (pThis->enmProtocol == PS2M_PROTO_IMPS2)
     616        {
     617            /* NB: Only uses 4-bit dZ range, despite using a full byte. */
    550618            ps2kInsertQueue(pQueue, dZ);
     619            pThis->iAccumZ -= dZ;
     620        }
     621        else if (pThis->enmProtocol == PS2M_PROTO_IMEX)
     622        {
     623           /* Z value uses 4 bits; buttons 4/5 in bits 4 and 5. */
     624           val  = (fBtnState & PS2M_IMEX_BTN_MASK) << 1;
     625           val |= dZ & 0x0f;
     626           pThis->iAccumZ -= dZ;
     627           ps2kInsertQueue(pQueue, val);
     628        }
    551629        else
    552630        {
    553             Assert(pThis->enmProtocol == PS2M_PROTO_IMEX);
    554             /* Z value uses 4 bits; buttons 4/5 in bits 4 and 5. */
    555             val  = dZ & 0x0f;
    556             val |= (fBtnState << 1) & (RT_BIT(4) | RT_BIT(5));
     631            Assert((pThis->enmProtocol == PS2M_PROTO_IMEX_HORZ));
     632            /* With ImEx + horizontal reporting, prioritize buttons 4/5. */
     633            if (pThis->iAccumZ || pThis->iAccumW)
     634            {
     635               /* ImEx + horizontal reporting Horizontal scroll has
     636                * precedence over vertical. Buttons cannot be reported
     637                * this way.
     638                */
     639               if (pThis->iAccumW)
     640               {
     641                  dW = RT_MIN(RT_MAX(pThis->iAccumW, -32), 31);
     642                  val = (dW & 0x3F) | 0x40;
     643                  pThis->iAccumW -= dW;
     644               }
     645               else
     646               {
     647                  Assert(pThis->iAccumZ);
     648                  /* We can use 6-bit dZ range. Wow! */
     649                  dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -32), 31);
     650                  val = (dZ & 0x3F) | 0x80;
     651                  pThis->iAccumZ -= dZ;
     652               }
     653            }
     654            else
     655            {
     656               /* Just Buttons 4/5 in bits 4 and 5. No scrolling. */
     657               val = (fBtnState & PS2M_IMEX_BTN_MASK) << 1;
     658            }
    557659            ps2kInsertQueue(pQueue, val);
    558660        }
     
    560662
    561663    /* Clear the movement accumulators, but not necessarily button state. */
    562     pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = 0;
     664    pThis->iAccumX = pThis->iAccumY = 0;
    563665    /* Clear accumulated button state only when it's being used. */
    564666    if (fAccumBtns)
    565667    {
    566         pThis->fReportedB = pThis->fAccumB;
     668        pThis->fReportedB = pThis->fCurrB | pThis->fAccumB;
    567669        pThis->fAccumB    = 0;
    568670    }
     
    675777        case ACMD_READ_ID:
    676778            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
    677             ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->enmProtocol);
     779            /* ImEx + horizontal is protocol 4, just like plain ImEx. */
     780            u8Val = pThis->enmProtocol == PS2M_PROTO_IMEX_HORZ ? PS2M_PROTO_IMEX : pThis->enmProtocol;
     781            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, u8Val);
    678782            pThis->u8CurrCmd = 0;
    679783            break;
     
    840944static uint32_t ps2mHaveEvents(PPS2M pThis)
    841945{
    842     return   pThis->iAccumX | pThis->iAccumY | pThis->iAccumZ
    843            | (pThis->fCurrB != pThis->fReportedB) | (pThis->fAccumB != 0);
     946    return   pThis->iAccumX || pThis->iAccumY || pThis->iAccumZ || pThis->iAccumW
     947           || ((pThis->fCurrB | pThis->fAccumB) != pThis->fReportedB);
    844948}
    845949
     
    9021006{
    9031007    static const char   *pcszModes[] = { "normal", "reset", "wrap" };
    904     static const char   *pcszProtocols[] = { "PS/2", NULL, NULL, "ImPS/2", "ImEx" };
     1008    static const char   *pcszProtocols[] = { "PS/2", NULL, NULL, "ImPS/2", "ImEx", "ImEx+horizontal" };
    9051009    PPS2M   pThis = KBDGetPS2MFromDevIns(pDevIns);
    9061010    NOREF(pszArgs);
     
    9131017                    pThis->u8State & AUX_STATE_ENABLED ? "enabled" : "disabled");
    9141018    pHlp->pfnPrintf(pHlp, "Protocol: %s, scaling %u:1\n",
    915                     pcszProtocols[pThis->enmProtocol], pThis->u8State & AUX_STATE_SCALING ? 2 : 1);
     1019                    pcszProtocols[pThis->enmProtocol],
     1020                    pThis->u8State & AUX_STATE_SCALING ? 2 : 1);
    9161021    pHlp->pfnPrintf(pHlp, "Active command %02X\n", pThis->u8CurrCmd);
    9171022    pHlp->pfnPrintf(pHlp, "Sampling rate %u reports/sec, resolution %u counts/mm\n",
     
    9531058                              int32_t dz, int32_t dw, uint32_t fButtons)
    9541059{
    955     RT_NOREF1(dw);
    9561060    int             rc = VINF_SUCCESS;
    9571061
    958     /* Update internal accumulators and button state. */
     1062    /* Update internal accumulators and button state. Ignore any buttons beyond 5. */
    9591063    pThis->iAccumX += dx;
    9601064    pThis->iAccumY += dy;
    9611065    pThis->iAccumZ += dz;
    962     pThis->fAccumB |= fButtons;     /// @todo accumulate based on current protocol?
    963     pThis->fCurrB   = fButtons;
    964 
    965     /* Report the event and start the throttle timer unless it's already running. */
    966     if (!pThis->fThrottleActive)
     1066    pThis->iAccumW += dw;
     1067    pThis->fCurrB   = fButtons & (PS2M_STD_BTN_MASK | PS2M_IMEX_BTN_MASK);
     1068    pThis->fAccumB |= pThis->fCurrB;
     1069
     1070    /* Ditch accumulated data that can't be reported by the current protocol.
     1071     * This avoids sending phantom empty reports when un-reportable events
     1072     * are received.
     1073     */
     1074    if (pThis->enmProtocol < PS2M_PROTO_IMEX_HORZ)
     1075       pThis->iAccumW = 0; /* No horizontal scroll. */
     1076
     1077    if (pThis->enmProtocol < PS2M_PROTO_IMEX)
     1078    {
     1079       pThis->fAccumB &= PS2M_STD_BTN_MASK;   /* Only buttons 1-3. */
     1080       pThis->fCurrB  &= PS2M_STD_BTN_MASK;
     1081    }
     1082
     1083    if (pThis->enmProtocol < PS2M_PROTO_IMPS2)
     1084       pThis->iAccumZ = 0; /* No vertical scroll. */
     1085
     1086    /* Report the event (if any) and start the throttle timer unless it's already running. */
     1087    if (!pThis->fThrottleActive && ps2mHaveEvents(pThis))
    9671088    {
    9681089        ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->evtQ, true);
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette