2

У меня есть дешевая беспроводная мышь Logitech M215 уже много лет. Это все еще работает отлично.

Для своего ноутбука я приобрел пару дополнительных мышей - оба Logitech - ожидающих одинаковую производительность. Проблема: вертикальная чувствительность неудобна для обоих. Чувствительность вдоль оси х велика, но чувствительность вдоль оси у медленная до такой степени, что я ненавижу их использовать. (Эта проблема повторяется на моем настольном ПК.)

Я знаю, как настроить скорость указателя в Windows, но я не собираюсь масштабировать обе оси - только ось Y. Очевидно, есть способ сделать это в Ubuntu, и я собрал множество форумов, на которых, по сути, задают тот же вопрос, который я задаю сейчас, но безрезультатно. Этот вопрос даже задавался здесь, но комментарии не помогли.

Кто-нибудь знает способ сделать это в Windows 8.1? Мне удобно вносить любые изменения в реестр / загружать любое программное обеспечение, которое может помочь.

5 ответов5

2

У меня есть M310, и я имею дело с той же проблемой. Некоторые люди добились успеха, очистив объектив сжатым воздухом. Для меня это был мой коврик для мыши ... Поверните ваш планшет на 90 градусов и посмотрите, есть ли эффект.

0

Вот некоторый код, который может как повышать, так и понижать чувствительность для каждого устройства (можно уменьшить чувствительность одной мыши, но не повлиять на другую).
Он использует RawInput (реализован в C # DLL для скорости) и SetWindowsHookEx (реализован в чистом AHK) для блокировки.

Обратите внимание, что я только недавно получил этот метод для работы, так что вполне могут быть проблемы и вещи, которые можно улучшить. У меня есть ветка здесь, на форумах AHK, где подробно описываются мои усилия - там могут быть более свежие или многофункциональные версии.

Код C #
Запустите новый проект библиотеки классов, добавьте ссылку на SharpDX.RawInput через NuGet DLL, которая должна называться MouseDelta.dll и должна находиться в той же папке.
При сборке он должен выплевывать две DLL-библиотеки SharpDX в папку сборки - они понадобятся и вашему скрипту.

using System;
using System.Windows.Forms;
using SharpDX.Multimedia;
using SharpDX.RawInput;
using System.Threading;
using System.Collections.Generic;

public class MouseDelta
{
    private readonly Thread messagePump;

    public dynamic relativeMoveCallback;
    public dynamic wheelCallback;

    static private Dictionary<IntPtr, string> seenMice = new Dictionary<IntPtr, string>();
    static private string subscribedMouse = null;

    private AutoResetEvent messagePumpRunning = new AutoResetEvent(false);

    public MouseDelta()
    {
        // start message pump in its own thread  
        messagePump = new Thread(RunMessagePump) { Name = "ManualMessagePump" };
        messagePump.Start();
        messagePumpRunning.WaitOne();
    }

    public void SubscribeRelativeMove(dynamic callback, string mouseId = null)
    {
        SetSubscribedMouse(mouseId);
        relativeMoveCallback = callback;
    }

    public void SubscribeWheel(dynamic callback, string mouseId = null)
    {
        SetSubscribedMouse(mouseId);
        wheelCallback = callback;
    }

    private void SetSubscribedMouse(string mouseId)
    {
        if (mouseId != null)
        {
            subscribedMouse = mouseId == "0" ? null : mouseId;
        }
    }

    // the message pump thread  
    private void RunMessagePump()
    {
        // Create control to handle windows messages   
        MessageHandler messageHandler = new MessageHandler();

        // Register for RawInput mouse messages
        Device.RegisterDevice(UsagePage.Generic, UsageId.GenericMouse, DeviceFlags.InputSink, messageHandler.Handle);
        Device.MouseInput += ProcessMouseInput;

        messagePumpRunning.Set();
        Application.Run();
    }

    private void ProcessMouseInput(object sender, MouseInputEventArgs args)
    {
        //Console.WriteLine(string.Format("(x,y):({0},{1}) Buttons: {2} State: {3} Wheel: {4}\r\n", args.X, args.Y, args.ButtonFlags, args.Mode, args.WheelDelta));
        // Handle mouse filtering
        if (!seenMice.ContainsKey(args.Device))
        {
            DeviceInfo info = null;
            var devices = Device.GetDevices();
            foreach (var dev in devices)
            {
                if (dev.Handle == args.Device)
                {
                    info = dev;
                    break;
                }
            }
            if (info == null)
                return;
            string item = info.DeviceName;
            item = item.Substring(4);

            string[] split = item.Split('#');

            //string id_01 = split[0];    // ACPI (Class code)
            string id_02 = split[1];    // PNP0303 (SubClass code)
                                        //string id_03 = split[2];    // 3&13c0b0c5&0 (Protocol code)

            seenMice.Add(args.Device, id_02);
        }

        if (subscribedMouse != null && subscribedMouse != seenMice[args.Device])
        {
            return;
        }

        // Fire appropriate Callback
        if (args.Mode == MouseMode.MoveRelative && relativeMoveCallback != null && (Math.Abs(args.X) + Math.Abs(args.Y) > 0))
        {
            relativeMoveCallback(args.X, args.Y, seenMice[args.Device]);
        }
        else if (args.WheelDelta != 0 && wheelCallback != null)
        {
            wheelCallback(args.WheelDelta / 120, seenMice[args.Device]);
        }
    }
}

// Useful SO post on handling messages - code for overriding WndProc
// https://stackoverflow.com/questions/2443867/message-pump-in-net-windows-service
// Although the above code is not quite complete. This blog post has the implementation for MessageData
// http://joe-bq-wang.iteye.com/blog/1882661

// However, by overriding WndProc, we have to process all messages, and then you do not get a SharpDX object..
// ... you just appear to get a raw WM_INPUT message

// For now, this seems to serve our purposes
internal class MessageHandler : NativeWindow
{
    public MessageHandler()
    {
        CreateHandle(new CreateParams());
    }
}

Основной скрипт AHK (вам нужно только отредактировать это)

; ================= USER SCRIPT ================
#SingleInstance force
#NoEnv
#include CLR.ahk
#include MouseDelta.ahk
OnExit, UnhookAndClose

GoSub, Hook

Gui, Add, Text, , Select Mouse:
mdw := new MouseDeltaWrapper("x+5 yp-3 w200")
mdw.SubscribeMove(Func("MoveEvent"))
mdw.SubscribeWheel(Func("WheelEvent"))
Gui, Show
return

^Esc::
UnhookAndClose:
GuiClose:
    GoSub, UnHook
    ExitApp

Hook:
    hHookMouse := SetWindowsHookEx(WH_MOUSE_LL  := 14, RegisterCallback("MouseMove", "Fast"))
    return

UnHook:
    UnhookWindowsHookEx(hHookMouse)
    return

MoveEvent(x, y, mouseId){
    Global mdw
    if (mdw.SelectedMouse == 0 || mdw.SelectedMouse == mouseId){
        DllCall("mouse_event",uint,1,int, x ,int, y,uint,0,int,0)
    }
}

WheelEvent(value, mouseId){
    ToolTip % "Wheel: " value ", ID: " mouseId
}

AHK Wrapper
Сохранить как MouseDelta.ahk в той же папке

; ================= WRAPPER LIBRARY ================
class MouseDeltaWrapper {
    SeenMice := {}
    SelectedMouse := 0
    MoveCallback := 0

    __New(guiOptions := "", dllPath := "MouseDelta.dll"){
        this.Callback := callback

        Gui, +HwndHwnd
        this.GuiHwnd := Hwnd

        Gui, Add, DDL, % "hwndhDDL " guiOptions, Any||
        this.hDDL := hDDL

        fn := this._UserSelectedMouse.Bind(this)
        GuiControl, +g, % this.hDDL, % fn

        asm := CLR_LoadLibrary(dllPath)
        md := asm.CreateInstance("MouseDelta")

        md.SubscribeRelativeMove(this._MoveEvent.Bind(this))
        md.SubscribeWheel(this._WheelEvent.Bind(this))
        this.md := md

        this._UserSelectedMouse()
    }

    SubscribeMove(callback){
        this.MoveCallback := callback
    }

    SubscribeWheel(callback){
        this.WheelCallback := callback
    }

    _UserSelectedMouse(){
        GuiControlGet, mouseId, , % this.hDDL
        this.SelectedMouse := mouseId == "Any" ? 0 : mouseId
        if (this.MoveCallback != 0)
            this.md.SubscribeRelativeMove(this._MoveEvent.Bind(this), this.SelectedMouse)
        if (this.WheelCallback != 0)
            this.md.SubscribeWheel(this._WheelEvent.Bind(this), this.SelectedMouse)
    }

    _AddMouseToDDL(mouseId){
        GuiControl, , % this.hDDL, % mouseId
    }

    _UpdateMice(mouseId){
        if (!this.SeenMice.HasKey(mouseId)){
            this.SeenMice[mouseId] := 1
            this._AddMouseToDDL(mouseId)
        }
    }

    _MoveEvent(x, y, mouseId){
        this._UpdateMice(mouseId)
        if (this.MoveCallback != 0 && (this.SelectedMouse == 0 || this.SelectedMouse == mouseId)){
            this.MoveCallback.Call(x, y, mouseId)
        }
    }

    _WheelEvent(value, mouseId){
        this._UpdateMice(mouseId)
        if (this.WheelCallback != 0 && (this.SelectedMouse == 0 || this.SelectedMouse == mouseId)){
            this.WheelCallback.Call(value, mouseId)
        }
    }
}

MouseMove(nCode, wParam, lParam)
{
    Critical
    SetFormat, Integer, D
    If !nCode && (wParam = 0x200){
        ; Mouse movement - process
        if (NumGet(lParam+0, 12, "int")){
            ; if the LLMHF_INJECTED flag is set, this is "injected" input (Came from mouse_event)
            ; Let this input through
            Return CallNextHookEx(nCode, wParam, lParam)
        } else {
            ; Block the input
            Return 1
        }
    } else {
        ; Other mouse message - let through
        Return CallNextHookEx(nCode, wParam, lParam)
    }
}

SetWindowsHookEx(idHook, pfn)
{
    ;Return DllCall("SetWindowsHookEx", "int", idHook, "Uint", pfn, "Uint", DllCall("GetModuleHandle", "Uint", 0), "Uint", 0)
    ;The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process
    DllCall("SetWindowsHookEx", "int", idHook, "Uint", pfn, "Uint", 0, "Uint", 0)
}

UnhookWindowsHookEx(hHook)
{
    Return DllCall("UnhookWindowsHookEx", "Uint", hHook)
}

CallNextHookEx(nCode, wParam, lParam, hHook = 0)
{
    Return DllCall("CallNextHookEx", "Uint", hHook, "int", nCode, "Uint", wParam, "Uint", lParam)
}

Библиотека CLR - позволяет AHK взаимодействовать с C # DLL.
Сохраните его как CLR.ahk в той же папке или в папке AHK Lib.

; ==========================================================
;                  .NET Framework Interop
;      http://www.autohotkey.com/forum/topic26191.html
; ==========================================================
;
;   Author:     Lexikos
;   Version:    1.2
;   Requires:   AutoHotkey_L v1.0.96+
;
; Modified by evilC for compatibility with AHK_H as well as AHK_L
; "null" is a reserved word in AHK_H, so did search & Replace from "null" to "_null"
CLR_LoadLibrary(AssemblyName, AppDomain=0)
{
    if !AppDomain
        AppDomain := CLR_GetDefaultDomain()
    e := ComObjError(0)
    Loop 1 {
        if assembly := AppDomain.Load_2(AssemblyName)
            break
        static _null := ComObject(13,0)
        args := ComObjArray(0xC, 1),  args[0] := AssemblyName
        typeofAssembly := AppDomain.GetType().Assembly.GetType()
        if assembly := typeofAssembly.InvokeMember_3("LoadWithPartialName", 0x158, _null, _null, args)
            break
        if assembly := typeofAssembly.InvokeMember_3("LoadFrom", 0x158, _null, _null, args)
            break
    }
    ComObjError(e)
    return assembly
}

CLR_CreateObject(Assembly, TypeName, Args*)
{
    if !(argCount := Args.MaxIndex())
        return Assembly.CreateInstance_2(TypeName, true)

    vargs := ComObjArray(0xC, argCount)
    Loop % argCount
        vargs[A_Index-1] := Args[A_Index]

    static Array_Empty := ComObjArray(0xC,0), _null := ComObject(13,0)

    return Assembly.CreateInstance_3(TypeName, true, 0, _null, vargs, _null, Array_Empty)
}

CLR_CompileC#(Code, References="", AppDomain=0, FileName="", CompilerOptions="")
{
    return CLR_CompileAssembly(Code, References, "System", "Microsoft.CSharp.CSharpCodeProvider", AppDomain, FileName, CompilerOptions)
}

CLR_CompileVB(Code, References="", AppDomain=0, FileName="", CompilerOptions="")
{
    return CLR_CompileAssembly(Code, References, "System", "Microsoft.VisualBasic.VBCodeProvider", AppDomain, FileName, CompilerOptions)
}

CLR_StartDomain(ByRef AppDomain, BaseDirectory="")
{
    static _null := ComObject(13,0)
    args := ComObjArray(0xC, 5), args[0] := "", args[2] := BaseDirectory, args[4] := ComObject(0xB,false)
    AppDomain := CLR_GetDefaultDomain().GetType().InvokeMember_3("CreateDomain", 0x158, _null, _null, args)
    return A_LastError >= 0
}

CLR_StopDomain(ByRef AppDomain)
{   ; ICorRuntimeHost::UnloadDomain
    DllCall("SetLastError", "uint", hr := DllCall(NumGet(NumGet(0+RtHst:=CLR_Start())+20*A_PtrSize), "ptr", RtHst, "ptr", ComObjValue(AppDomain))), AppDomain := ""
    return hr >= 0
}

; NOTE: IT IS NOT NECESSARY TO CALL THIS FUNCTION unless you need to load a specific version.
CLR_Start(Version="") ; returns ICorRuntimeHost*
{
    static RtHst := 0
    ; The simple method gives no control over versioning, and seems to load .NET v2 even when v4 is present:
    ; return RtHst ? RtHst : (RtHst:=COM_CreateObject("CLRMetaData.CorRuntimeHost","{CB2F6722-AB3A-11D2-9C40-00C04FA30A3E}"), DllCall(NumGet(NumGet(RtHst+0)+40),"uint",RtHst))
    if RtHst
        return RtHst
    EnvGet SystemRoot, SystemRoot
    if Version =
        Loop % SystemRoot "\Microsoft.NET\Framework" (A_PtrSize=8?"64":"") "\*", 2
            if (FileExist(A_LoopFileFullPath "\mscorlib.dll") && A_LoopFileName > Version)
                Version := A_LoopFileName
    if DllCall("mscoree\CorBindToRuntimeEx", "wstr", Version, "ptr", 0, "uint", 0
    , "ptr", CLR_GUID(CLSID_CorRuntimeHost, "{CB2F6723-AB3A-11D2-9C40-00C04FA30A3E}")
    , "ptr", CLR_GUID(IID_ICorRuntimeHost,  "{CB2F6722-AB3A-11D2-9C40-00C04FA30A3E}")
    , "ptr*", RtHst) >= 0
        DllCall(NumGet(NumGet(RtHst+0)+10*A_PtrSize), "ptr", RtHst) ; Start
    return RtHst
}

;
; INTERNAL FUNCTIONS
;

CLR_GetDefaultDomain()
{
    static defaultDomain := 0
    if !defaultDomain
    {   ; ICorRuntimeHost::GetDefaultDomain
        if DllCall(NumGet(NumGet(0+RtHst:=CLR_Start())+13*A_PtrSize), "ptr", RtHst, "ptr*", p:=0) >= 0
            defaultDomain := ComObject(p), ObjRelease(p)
    }
    return defaultDomain
}

CLR_CompileAssembly(Code, References, ProviderAssembly, ProviderType, AppDomain=0, FileName="", CompilerOptions="")
{
    if !AppDomain
        AppDomain := CLR_GetDefaultDomain()

    if !(asmProvider := CLR_LoadLibrary(ProviderAssembly, AppDomain))
    || !(codeProvider := asmProvider.CreateInstance(ProviderType))
    || !(codeCompiler := codeProvider.CreateCompiler())
        return 0

    if !(asmSystem := (ProviderAssembly="System") ? asmProvider : CLR_LoadLibrary("System", AppDomain))
        return 0

    ; Convert | delimited list of references into an array.
    StringSplit, Refs, References, |, %A_Space%%A_Tab%
    aRefs := ComObjArray(8, Refs0)
    Loop % Refs0
        aRefs[A_Index-1] := Refs%A_Index%

    ; Set parameters for compiler.
    prms := CLR_CreateObject(asmSystem, "System.CodeDom.Compiler.CompilerParameters", aRefs)
    , prms.OutputAssembly          := FileName
    , prms.GenerateInMemory        := FileName=""
    , prms.GenerateExecutable      := SubStr(FileName,-3)=".exe"
    , prms.CompilerOptions         := CompilerOptions
    , prms.IncludeDebugInformation := true

    ; Compile!
    compilerRes := codeCompiler.CompileAssemblyFromSource(prms, Code)

    if error_count := (errors := compilerRes.Errors).Count
    {
        error_text := ""
        Loop % error_count
            error_text .= ((e := errors.Item[A_Index-1]).IsWarning ? "Warning " : "Error ") . e.ErrorNumber " on line " e.Line ": " e.ErrorText "`n`n"
        MsgBox, 16, Compilation Failed, %error_text%
        return 0
    }
    ; Success. Return Assembly object or path.
    return compilerRes[FileName="" ? "CompiledAssembly" : "PathToAssembly"]
}

CLR_GUID(ByRef GUID, sGUID)
{
    VarSetCapacity(GUID, 16, 0)
    return DllCall("ole32\CLSIDFromString", "wstr", sGUID, "ptr", &GUID) >= 0 ? &GUID : ""
}
0

Существует ли какое-либо специальное программное обеспечение для мышей Logitech, подобное Razer Synapse? Вы можете найти там настройки пользовательской чувствительности.

0

Вот вариант, который не идеален, но помогает. Первоначально он был предоставлен пользователем Nextron по адресу https://autohotkey.com/board/topic/13531-adjusting-mouse-sensitivity-via-hotkey/.

Этот скрипт дает вам возможность независимо изменять чувствительность X и Y в Windows и работает со всеми мышами и всеми версиями Windows. Он не идеален в том смысле, что вы должны использовать целочисленные кратные чувствительности, и он работает путем умножения импульсов движения мыши.

  1. Установите автоотключение.
  2. Измените приведенный ниже скрипт autohotkey. Там, где написано new MouseAccelerator(0, 1) , измените его на new MouseAccelerator (<amount to multiply X sensitivity by> - 1, <amount to multiply Y sensitivity by> - 1) . Например, MouseAccelerator(0, 1) удваивает движение Y, но не влияет на движение X, MouseAccelerator(2, 1) утраивает горизонтальное движение и удваивает вертикальное движение и т.д.
  3. Запустите измененный скрипт. Нажмите F12, когда хотите выключить его.

    ;The SendInput DllCall is specifically 32-bit. So check for the correct bitness of AutoHotkey and if not, try to run the right one.
    If (A_PtrSize=8){
        SplitPath, A_AhkPath,,Dir
        Run %Dir%\AutoHotkeyU32.exe %A_ScriptFullPath%
        ExitApp
    }
    
    ;Call below to accelerate the mouse input. The first two parameters are the integer factors of artificial amplification added on top of the physical input.
    ;The first is for horizontal/x-axis movement, the second for vertical/y-axis movement.
    new MouseAccelerator(0, 1)
    
    F12::ExitApp
    
    
    ; Gets called when mouse moves or stops
    ; x and y are DELTA moves (Amount moved since last message), NOT coordinates.
    MouseAcceleratorEvent(x := 0, y := 0, accelerationx := 2, accelerationy := 2){
        static MouseAcceleratorPaused
        If !(MouseAcceleratorPaused){
            MouseAcceleratorPaused:=true
            VarSetCapacity( MouseInput, 28, 0 )
            NumPut( x * accelerationx, MouseInput, 4, "Int" ) ; dx
            NumPut( y * accelerationy, MouseInput, 8, "Int" ) ; dy
            NumPut( 0x0001, MouseInput, 16, "UInt" ) ; MOUSEEVENTF_MOVE = 0x0001
            DllCall("SendInput", "UInt", 1, "UInt", &MouseInput, "Int", 28 )
            sleep,-1
            MouseAcceleratorPaused:=false
        }
    }
    
    ; ================================== LIBRARY ========================================
    ; Instantiate this class and pass it a func name or a Function Object
    ; The specified function will be called with the delta move for the X and Y axes
    ; Normally, there is no windows message "mouse stopped", so one is simulated.
    ; After 10ms of no mouse movement, the callback is called with 0 for X and Y
    ; https://autohotkey.com/boards/viewtopic.php?f=19&t=10159
    Class MouseAccelerator {
        __New(accelerationx:=2, accelerationy:=2, callback:="MouseAcceleratorEvent"){
            static DevSize := 8 + A_PtrSize
            static RIDEV_INPUTSINK := 0x00000100
    
            this.TimeoutFn := this.TimeoutFunc.Bind(this)
    
            this.Callback := callback
            this.Accelerationx := accelerationx
            this.Accelerationy := accelerationy
            ; Register mouse for WM_INPUT messages.
            VarSetCapacity(RAWINPUTDEVICE, DevSize)
            NumPut(1, RAWINPUTDEVICE, 0, "UShort")
            NumPut(2, RAWINPUTDEVICE, 2, "UShort")
            NumPut(RIDEV_INPUTSINK, RAWINPUTDEVICE, 4, "Uint")
            ; WM_INPUT needs a hwnd to route to, so get the hwnd of the AHK Gui.
            ; It doesn't matter if the GUI is showing, it still exists
            Gui +hwndhwnd
            NumPut(hwnd, RAWINPUTDEVICE, 8, "Uint")
    
            this.RAWINPUTDEVICE := RAWINPUTDEVICE
            DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
            fn := this.MouseMoved.Bind(this)
            OnMessage(0x00FF, fn)
        }
    
        __Delete(){
            static RIDEV_REMOVE := 0x00000001
            static DevSize := 8 + A_PtrSize
            RAWINPUTDEVICE := this.RAWINPUTDEVICE
            NumPut(RIDEV_REMOVE, RAWINPUTDEVICE, 4, "Uint")
            DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
        }
    
        ; Called when the mouse moved.
        ; Messages tend to contain small (+/- 1) movements, and happen frequently (~20ms)
        MouseMoved(wParam, lParam){
            ; RawInput statics
            static DeviceSize := 2 * A_PtrSize, iSize := 0, sz := 0, offsets := {x: (20+A_PtrSize*2), y: (24+A_PtrSize*2)}, uRawInput
    
            static axes := {x: 1, y: 2}
    
            ; Find size of rawinput data - only needs to be run the first time.
            if (!iSize){
                r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", iSize, "UInt", 8 + (A_PtrSize * 2))
                VarSetCapacity(uRawInput, iSize)
            }
            sz := iSize ; param gets overwritten with # of bytes output, so preserve iSize
            ; Get RawInput data
            r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", &uRawInput, "UInt*", sz, "UInt", 8 + (A_PtrSize * 2))
    
            x := NumGet(&uRawInput, offsets.x, "Int")
            y := NumGet(&uRawInput, offsets.y, "Int")
    
            this.Callback.(x, y, this.Accelerationx, this.Accelerationy)
    
            ; There is no message for "Stopped", so simulate one
            fn := this.TimeoutFn
            SetTimer, % fn, -10
        }
    
        TimeoutFunc(){
            this.Callback.(0, 0)
        }
    
    }
    
0

Я хочу поставить здесь решение моего аналогичного случая. У меня был довольно дисбаланс между вертикальной и горизонтальной чувствительностью.

Наконец, я снял флажок "Повышение точности указателя" в "Свойствах мыши" и восстановил баланс.

Это было совершенно неясно. Поскольку я потратил много времени и почти случайно нашел решение, я бы хотел добавить его сюда.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .