Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support has_event_type from libevdev #10

Open
mjepronk opened this issue Oct 27, 2020 · 12 comments
Open

Support has_event_type from libevdev #10

mjepronk opened this issue Oct 27, 2020 · 12 comments

Comments

@mjepronk
Copy link

First, thanks for making this library open source!

I'm using it to create a simple background utility to disable my keyboard's and mouse/touchpad devices when needed.
It would be nice if I could use this library to easily enumerate all keyboard and mouse input devices.
For that, I think I need libevdev_has_event_type.

Would you be willing to add this?
If so, I can see if I can create a PR for it (I'm not really familiar with Haskell's FFI, but I can try... ;-)

@georgefst
Copy link
Owner

Yeah, I'd been meaning to add this.

63603f6

@mjepronk
Copy link
Author

Thanks a lot! I'm going to try it out.

@mjepronk mjepronk reopened this Oct 30, 2020
@mjepronk
Copy link
Author

It works great. However, it seems that it's not enough to determine if a device is a Keyboard or a Mouse.

For that libevdev_has_event_code would come in handy... Would be great if you could add that one too :-)

@georgefst
Copy link
Owner

georgefst commented Oct 30, 2020

Yeah, I wasn't quite sure what to do with that one as it doesn't really fit the high-level API nicely. You'll see that I added deviceHasEvent but on reflection I think I'll probably remove it. (EDIT: changed my mind on this)

It would be trivial to add a Device -> EventType -> EventCode -> IO Bool if that's what you want. I'd just want to put it under the "lower-level stuff" section, since EventCode isn't generally used with the more common event types.

Out of interest, which event codes are you looking for? I'm not sure there's a foolproof way to distinguish device types with just the evdev interface, but, based on my machine, the presence of EV_REL seems as good as any.

@mjepronk
Copy link
Author

Yeah, Device -> EventType -> EventCode -> IO Bool would be nice, but I think deviceHasEvent suffices (sorry that I missed that one, maybe add the Code suffix to the name to stay closer to the C API?).

My usecase is to identify all keyboard and mouse devices. In the libevdev documentation there is some example code to detect a mouse:

if (!libevdev_has_event_type(dev, EV_REL) ||
    !libevdev_has_event_code(dev, EV_KEY, BTN_LEFT)) {
        printf("This device does not look like a mouse\n");
        exit(1);
}

I'm not sure how to detect a keyboard. I think using EV_KEY and KEY_A or something.

@mjepronk
Copy link
Author

mjepronk commented Nov 2, 2020

You were right... I needed:

deviceHasEventCode :: Device -> EventType -> EventCode -> IO Bool
deviceHasEventCode dev e code = LL.hasEventCode (cDevice dev) (fromEnum' e) (fromEnum' code)

Would be great if you could add it! LowLevel.hs currently is a private module, so you would need to make it public if you want to put it there.

@mjepronk
Copy link
Author

mjepronk commented Nov 2, 2020

Hmm, it means I also would need to have a function to construct an EventCode from a Key, altough I could provide it myself:

import Evdev (EventCode(..))

eventCodeFromKey :: Key -> EventCode
eventCodeFromKey = EventCode . fromIntegral . fromEnum

@georgefst
Copy link
Owner

LowLevel.hs currently is a private module, so you would need to make it public if you want to put it there.

I meant that I'd place it alongside the already-exposed lower-level stuff. In the long run, I would like to expose more of the low-level functionality, but the existing LowLevel module really is only designed for internal use - it's unsafe, undocumented and inconsistent. I don't quite know yet what some of the details of the lower-level API would look like, but it would probably make sense to include functions like eventCodeFromKey :: Key -> EventCode rather than requiring the end-user to ever need fromEnum/toEnum.

Perhaps it's time I started on that. I'm certainly overdue to return to this library, since I added a lot of stuff a few months ago that hasn't made it on to Hackage.

As for your immediate issue, the udev API is the 'right' way to get device types. The Haskell bindings may be an option (I've never used them so I can't really say). Otherwise you could hack something together with readProcess and isInfixOf: look for ID_INPUT_KEYBOARD, ID_INPUT_MOUSE etc. in the output of udevadm info -q property /dev/input/eventX.

@mjepronk
Copy link
Author

mjepronk commented Nov 2, 2020

LowLevel.hs currently is a private module, so you would need to make it public if you want to put it there.

I meant that I'd place it alongside the already-exposed lower-level stuff. In the long run, I would like to expose more of the low-level functionality, but the existing LowLevel module really is only designed for internal use - it's unsafe, undocumented and inconsistent. I don't quite know yet what some of the details of the lower-level API would look like, but it would probably make sense to include functions like eventCodeFromKey :: Key -> EventCode rather than requiring the end-user to ever need fromEnum/toEnum.

Aah OK, makes sense.

Perhaps it's time I started on that. I'm certainly overdue to return to this library, since I added a lot of stuff a few months ago that hasn't made it on to Hackage.

As for your immediate issue, the udev API is the 'right' way to get device types. The Haskell bindings may be an option (I've never used them so I can't really say). Otherwise you could hack something together with readProcess and isInfixOf: look for ID_INPUT_KEYBOARD, ID_INPUT_MOUSE etc. in the output of udevadm info -q property /dev/input/eventX.

Yeah, right now I use the following code (with the functions I mentioned above) which works fine by the way:

data DeviceType
    = Keyboard
    | Mouse -- Mouse, TouchPad, TrackPoint, tablet
    | Other
    deriving Eq

enumerateDevices :: IO [(Device, DeviceType)]
enumerateDevices = do
    ds <- fmap C8.pack <$> listDirectory (C8.unpack evdevDir)
    traverse go (filter ("event" `C8.isPrefixOf`) ds)
  where
    go path = do
        d <- newDevice (evdevDir <> "/" <> path)
        types <- deviceEventTypes d
        hasAKey <- deviceHasEventCode d EvKey (eventCodeFromKey KeyA)
        hasLftBtn <- deviceHasEventCode d EvKey (eventCodeFromKey BtnLeft)
        case () of
          () | hasAKey -> pure (d, Keyboard)
             | EvRel `elem` types && hasLftBtn -> pure (d, Mouse)
             | EvAbs `elem` types && hasLftBtn -> pure (d, Mouse)
             | otherwise -> pure (d, Other)

@georgefst
Copy link
Owner

georgefst commented Nov 2, 2020

Yeah cool, that'll probably work 99% of the time. I just wanted to point out that Linux has more robust ways of doing it.

By the way, you could save some packing and unpacking etc. by using the ByteString-friendly versions of packages:

I should probably point to those from the README...

Also, even more off-topic, but in case you haven't seen it: MultiWayIf is cool.

@georgefst
Copy link
Owner

I've thought about this, and I like it the way it is now. In particular, stuff like deviceHasEventCode feels inconsistent with the existing API.

For what it's worth, you could write the relevant lines of the example as:

hasAKey <- deviceHasEvent d (KeyEvent KeyA Pressed)
hasLftBtn <- deviceHasEvent d (KeyEvent BtnLeft Pressed)

@georgefst
Copy link
Owner

I might consider adding an EventCode typeclass, of which Key would be an instance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants