Coder Social home page Coder Social logo

evdev's Introduction

evdev

GitHub Workflow Status Crates.io

Documentation

Nice(r) access to evdev devices.

What is evdev?

evdev is the Linux kernel's generic input interface, also implemented by other kernels such as FreeBSD.

libevdev is a userspace library written in c for interacting with this system in a high level way rather than using ioctl system calls directly.

This crate is a re-implementation of libevdev in rust. There is some trickery involved, so please read the crate documentation.

There is also an alternative crate: evdev-rs which wraps libevdev instead.

Overview

This crate provides functionality for reading streams of events from input devices.

Like libevdev, this crate also provides functionality for interacting with uinput. Uinput is a kernel module which allows virtual input devices to be created from userspace.

Synchronization

This library exposes raw evdev events, but uses the Rust Iterator trait to do so. When processing events via fetch_events, the library will handle SYN_DROPPED events by injecting fake state updates in an attempt to ensure callers see state transition messages consistent with actual device state. When processing via *_no_sync this correction is not done, and SYN_DROPPED messages will appear if the kernel ring buffer is overrun before messages are read. I try to match libevdev closely, where possible.

Limitations

There is no abstraction for gamepad-like devices that allows mapping button numbers to logical buttons, nor is one planned. Such a thing should take place in a higher-level crate, likely supporting multiple platforms.

Example

Plenty of nice examples of how to use this crate can be found in the examples directory of this repository. If you feel like an example of how to use a certain part of the evdev crate is missing, then feel free to open a pull request.

A good introduction is the evtest.rs example (which roughly corresponds to the userspace evtest tool.

Releases

Detailed release notes are available in this repository at CHANGELOG.md.

evdev's People

Contributors

a6-webm avatar antoineprv avatar anytimetraveler avatar buzztaiki avatar conqp avatar coolreader18 avatar denispeplin avatar emberian avatar floriannadam avatar hex-a-tom avatar homeworkprod avatar jeff-hiner avatar jonasboss avatar jtroo avatar lehmanju avatar linuscde avatar maccraft123 avatar mbway avatar nicokoch avatar ninevra avatar nowakf avatar philipp-m avatar ralith avatar seanyoung avatar stephanvanschaik avatar toxicmushroom avatar valpackett avatar waynr avatar whot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

evdev's Issues

Mismatched types when compiling for arm

When compiling for armv7-unknown-linux-musleabihf, I get two errors like below:

error[E0308]: mismatched types
   --> /cargo/registry/src/github.com-1ecc6299db9ec823/evdev-0.10.2/src/raw.rs:216:47 nix, num-bigint                                                                                                                                
    |
216 |     convert_ioctl_res!(::nix::libc::ioctl(fd, ior!(b'E', 0x20 + ev, len) as ::libc::c_ulong, buf))
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32`
    |
help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit
    |
216 |     convert_ioctl_res!(::nix::libc::ioctl(fd, (ior!(b'E', 0x20 + ev, len) as ::libc::c_ulong).try_into().unwrap(), buf))
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Device::into_event_stream() drops device on error

If you call Device::into_event_stream() and it fails, the device is dropped and has no way to recover it. If you would like to use any information off of the Device struct in your error handling, you have to clone the relevant information before even calling Device::into_event_stream. The solution to this would be to have an IntoEventStreamError which includes the underlying io::Error as well as the Device, allowing users to query information off of the device in event of an error.

The solution is to create a custom error type which contains the underlying device. Unfortunately, AsyncFd::new has the same issue, so this would be reliant on Tokio doing the same thing. I will create an issue upstream as well.

Keys aren't properly recognized in other keyboards

I am using Dvorak keyboard and xremap only acknowledges what keys are pressed based on the US QWERTY keyboard.

For example, if I want to have combo C-s t to launch Firefox, I have to configure it as:

keymap:
  - name: general keybindings
    remap:
      leftctrl-semicolon:
        remap:
          k:
            launch: ["firefox"]

eviocgbit uses bit count instead of byte count

Bit count is being used in a few places where a byte count is expected, like the eviocgbit length arguments. Having these use the size in bytes (from std::mem::size_of_val(x.as_mut_slice())) instead quieted warnings from valgrind. This is consistent with how libevdev uses sizeof.

For the most part I don't think this matters as the kernel uses the size of the structure as the max, but sometimes that size is one long longer than actually needed (or allocated by FixedBitSet).

Add grab and ungrab methods for Device

Hello,
I've read Evdev's documentation, and while I see the raw function for grabbing a device, there seems to be no safe abstraction.
Would it be possible to add grab and ungrab methods to Device struct?
It should take just few lines of code, and make grabbing significantly more ellegant.

Thanks!

v0.12.1 has the wrong `/dev/` path for `enumerate_dev_nodes_blocking`

Thanks for the work on this crate!

A user of my project discovered a bug in v0.12.1 where the dev node paths have changed since v0.12.0. It remains an iterator with a single entry as expected, but the single entry has an unexpected path.

jtroo/kanata#455

The symlink which Kanata creates for the virtual output device points at /sys/devices/virtual/input/input8/event1 which is a folder with a bunch of metadata rather than the expected /dev/input/event1.
The Created device /path/to/device message shows the same incorrect path.

Create InputEvent rom InputEventKind

In addition of the current constructors, a new constructor could be added:

fn from_kind(kind: impl Into<InputEventKind>, value);

impl From<Key> for InputEventKind {}
impl From<AbsoluteAxisType> for InputEventKind {}
...

InputEvent::from_kind(Key::BTN_A, 1);

Why are multitouch axes intentionally not requested?

When trying to use the input_absinfo for ABS_MT_POSITION_X/Y, I noticed them being 0. After stracing the program, I noticed that most absolute axes on another device with abs axes were grabbed fine with an eviocgabs ioctl. But not my multitouch ones which evtest could clearly find.

My first finding that 0x28 was not all the ABS values (would be 0x3f) didn't work. The I noticed the comment below the finding which explicitly ignored the ABS_MT values.

It seems that I can grab the axis without problem. What was the intention of "we'll handle that later.". Is there any extra checking that needs to be done for those that I missed? For now my fix seems to work just fine (LinusCDE@43fb0b3). I needed the fix for this code to work.

Implement Debug for Device

Debug is generally helpful for, well, debugging. Probably every struct should implement debug, but I particularly found myself wanting an implementation for Device. The rust docs even say: "Generally speaking, you should just derive a Debug implementation."

mismatched types in lib.rs

I get a compile-error in the lib.rs. I'm not sure if this is just a 32-bit system related issue.

error[E0308]: mismatched types
let dur = Duration::new(tv.tv_sec.unsigned_abs(), tv.tv_usec as u32 * 1000);
expected u64, found u32

evdev/src/lib.rs

Lines 391 to 398 in 14881c1

fn timeval_to_systime(tv: &libc::timeval) -> SystemTime {
let dur = Duration::new(tv.tv_sec.unsigned_abs(), tv.tv_usec as u32 * 1000);
if tv.tv_sec >= 0 {
SystemTime::UNIX_EPOCH + dur
} else {
SystemTime::UNIX_EPOCH - dur
}
}

Easier AttributeSet creation

Currently, to create an AttributeSet, we need to do a list on .insert or a loop. It would be nice to be able to do it via a constructor, for example AttributeSet::with_values(&[Key::BTN_A, Key::BTN_B]).

Use tags

It'd be nice if a git tag could be used to correspond to the Cargo.toml version changes, makes looking at the git log to grasp when things changed a lot easier.

If you're already using tags, maybe you forgot to git push origin $tagname? or git push --tags origin if you're happy to push all local tags at once.

Printing all evdev devices crashes with SIGILL

To reproduce:

extern crate evdev;

fn main() {
    for dev in evdev::enumerate() {
        println!("{}", dev);
    }
}

Perhaps it's specific to my hardware configuration.

It prints one of the results, then crashes. Here is a GDB session:

Program received signal SIGILL, Illegal instruction.
0x00005555555693c0 in <evdev::Key as core::fmt::Debug>::fmt (self=0x7fffffffd13c, f=0x7fffffffc578)
    at /home/score/.cargo/registry/src/github.com-1ecc6299db9ec823/evdev-0.10.1/src/scancodes.rs:5
5    #[derive(Copy, Clone, Debug)]
(gdb) x/10i $rip-0x11
   0x5555555693af <<evdev::Key as core::fmt::Debug>::fmt+19823>:    loopne 0x5555555693e1 <<evdev::Key as core::fmt::Debug>::fmt+19873>
   0x5555555693b1 <<evdev::Key as core::fmt::Debug>::fmt+19825>:    add    BYTE PTR [rax],al
   0x5555555693b3 <<evdev::Key as core::fmt::Debug>::fmt+19827>:    mov    rdx,rax
   0x5555555693b6 <<evdev::Key as core::fmt::Debug>::fmt+19830>:    call   0x5555555cd650 <core::fmt::Formatter::debug_tuple>
   0x5555555693bb <<evdev::Key as core::fmt::Debug>::fmt+19835>:    jmp    0x55555556c463 <<evdev::Key as core::fmt::Debug>::fmt+32291>
=> 0x5555555693c0 <<evdev::Key as core::fmt::Debug>::fmt+19840>:    ud2    
   0x5555555693c2 <<evdev::Key as core::fmt::Debug>::fmt+19842>:    mov    al,BYTE PTR [rsp+0x2f]
   0x5555555693c6 <<evdev::Key as core::fmt::Debug>::fmt+19846>:    and    al,0x1
   0x5555555693c8 <<evdev::Key as core::fmt::Debug>::fmt+19848>:    movzx  eax,al
   0x5555555693cb <<evdev::Key as core::fmt::Debug>::fmt+19851>:    add    rsp,0x30f8

And here is a screenshot of where the issue is reported in gdb:

I don't know enough rust to say for sure, but since it's showing the error at the top of the class (in source) and within <evdev::Key as core::fmt::Debug>::fmt (in asm), my guess is that something that isn't part of the enum was (unsafely?) coerced into an enum value, and now it's crashing when trying to look up the name.

Device implements `AsRawFd` but not `AsFd`

Rust at some point (not sure when, I'm quite new at this!) added AsFd as a safer alternative to AsRawFd. I'm using the nix crate which for some operations now expects either an OwnedFd or BorrowedFd.

(In my case specifically I'm using this will nix epoll bindings, which have recently (not yet released) been switched to use the new abstractions and the old API with RawFd has been deprecated.)

It would be nice if Device could implement AsFd (at least if the rust version is new enough) to allow this to work without unsafe code on my part.

Fails to build on FreeBSD

Regressed since de2c4e0. CC @unrelentingtech

$ rustc -vV
rustc 1.58.1
binary: rustc
commit-hash: unknown
commit-date: unknown
host: x86_64-unknown-freebsd
release: 1.58.1
LLVM version: 13.0.0

$ cargo build
[...]
   Compiling evdev v0.11.4 (/tmp/evdev)
error[E0432]: unresolved imports `libc::ff_effect`, `libc::input_absinfo`, `libc::input_id`, `libc::input_keymap_entry`, `libc::uinput_setup`
 --> src/sys.rs:2:12
  |
2 | use libc::{ff_effect, input_absinfo, input_id, input_keymap_entry, uinput_setup};
  |            ^^^^^^^^^  ^^^^^^^^^^^^^  ^^^^^^^^  ^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^ no `uinput_setup` in the root
  |            |          |              |         |
  |            |          |              |         no `input_keymap_entry` in the root
  |            |          |              no `input_id` in the root
  |            |          no `input_absinfo` in the root
  |            no `ff_effect` in the root

error[E0425]: cannot find value `EV_CNT` in crate `libc`
   --> src/constants.rs:46:43
    |
46  |     pub(crate) const COUNT: usize = libc::EV_CNT;
    |                                           ^^^^^^ help: a constant with a similar name exists: `AF_CNT`
    |
   ::: /home/foo/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.119/src/unix/bsd/freebsdlike/mod.rs:862:1
    |
862 | pub const AF_CNT: ::c_int = 21;
    | ------------------------------- similarly named constant `AF_CNT` defined here

error[E0425]: cannot find value `INPUT_PROP_CNT` in crate `libc`
  --> src/constants.rs:90:43
   |
90 |     pub(crate) const COUNT: usize = libc::INPUT_PROP_CNT;
   |                                           ^^^^^^^^^^^^^^ not found in `libc`

error[E0425]: cannot find value `REL_CNT` in crate `libc`
   --> src/constants.rs:116:43
    |
116 |     pub(crate) const COUNT: usize = libc::REL_CNT;
    |                                           ^^^^^^^ not found in `libc`

error[E0425]: cannot find value `ABS_CNT` in crate `libc`
   --> src/constants.rs:185:43
    |
185 |     pub(crate) const COUNT: usize = libc::ABS_CNT;
    |                                           ^^^^^^^ help: a constant with a similar name exists: `AF_CNT`
    |
   ::: /home/foo/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.119/src/unix/bsd/freebsdlike/mod.rs:862:1
    |
862 | pub const AF_CNT: ::c_int = 21;
    | ------------------------------- similarly named constant `AF_CNT` defined here

error[E0425]: cannot find value `SW_CNT` in crate `libc`
   --> src/constants.rs:232:43
    |
232 |     pub(crate) const COUNT: usize = libc::SW_CNT;
    |                                           ^^^^^^ help: a constant with a similar name exists: `AF_CNT`
    |
   ::: /home/foo/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.119/src/unix/bsd/freebsdlike/mod.rs:862:1
    |
862 | pub const AF_CNT: ::c_int = 21;
    | ------------------------------- similarly named constant `AF_CNT` defined here

error[E0425]: cannot find value `LED_CNT` in crate `libc`
   --> src/constants.rs:260:43
    |
260 |     pub(crate) const COUNT: usize = libc::LED_CNT;
    |                                           ^^^^^^^ not found in `libc`

error[E0425]: cannot find value `MSC_CNT` in crate `libc`
   --> src/constants.rs:285:43
    |
285 |     pub(crate) const COUNT: usize = libc::MSC_CNT;
    |                                           ^^^^^^^ not found in `libc`

error[E0425]: cannot find value `SND_CNT` in crate `libc`
   --> src/constants.rs:340:43
    |
340 |     pub(crate) const COUNT: usize = libc::SND_CNT;
    |                                           ^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_absinfo` in crate `libc`
  --> src/device_state.rs:12:44
   |
12 |     pub(crate) abs_vals: Option<Box<[libc::input_absinfo; AbsoluteAxisType::COUNT]>>,
   |                                            ^^^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_absinfo` in crate `libc`
  --> src/device_state.rs:89:46
   |
89 |     pub fn abs_vals(&self) -> Option<&[libc::input_absinfo]> {
   |                                              ^^^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_id` in crate `libc`
 --> src/inputid.rs:5:37
  |
5 | pub struct InputId(pub(crate) libc::input_id);
  |                                     ^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_id` in crate `libc`
 --> src/inputid.rs:7:17
  |
7 | impl From<libc::input_id> for InputId {
  |                 ^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_id` in crate `libc`
 --> src/inputid.rs:9:23
  |
9 |     fn from(id: libc::input_id) -> Self {
  |                       ^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_id` in crate `libc`
  --> src/inputid.rs:13:18
   |
13 | impl AsRef<libc::input_id> for InputId {
   |                  ^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_id` in crate `libc`
  --> src/inputid.rs:15:32
   |
15 |     fn as_ref(&self) -> &libc::input_id {
   |                                ^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_id` in crate `libc`
  --> src/inputid.rs:36:26
   |
36 |         Self::from(libc::input_id {
   |                          ^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_absinfo` in crate `libc`
  --> src/raw_stream.rs:36:27
   |
36 | const ABSINFO_ZERO: libc::input_absinfo = libc::input_absinfo {
   |                           ^^^^^^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_absinfo` in crate `libc`
  --> src/raw_stream.rs:36:49
   |
36 | const ABSINFO_ZERO: libc::input_absinfo = libc::input_absinfo {
   |                                                 ^^^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_absinfo` in crate `libc`
  --> src/raw_stream.rs:39:40
   |
39 | pub(crate) const ABS_VALS_INIT: [libc::input_absinfo; AbsoluteAxisType::COUNT] =
   |                                        ^^^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_id` in crate `libc`
  --> src/raw_stream.rs:56:15
   |
56 |     id: libc::input_id,
   |               ^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_event` in crate `libc`
  --> src/raw_stream.rs:69:37
   |
69 |     pub(crate) event_buf: Vec<libc::input_event>,
   |                                     ^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_event` in crate `libc`
   --> src/raw_stream.rs:401:67
    |
401 |         let num_read = bytes_read as usize / mem::size_of::<libc::input_event>();
    |                                                                   ^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_absinfo` in crate `libc`
   --> src/raw_stream.rs:429:54
    |
429 |     pub fn get_abs_state(&self) -> io::Result<[libc::input_absinfo; AbsoluteAxisType::COUNT]> {
    |                                                      ^^^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_absinfo` in crate `libc`
   --> src/raw_stream.rs:430:34
    |
430 |         let mut abs_vals: [libc::input_absinfo; AbsoluteAxisType::COUNT] = ABS_VALS_INIT;
    |                                  ^^^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_absinfo` in crate `libc`
   --> src/raw_stream.rs:466:31
    |
466 |         abs_vals: &mut [libc::input_absinfo; AbsoluteAxisType::COUNT],
    |                               ^^^^^^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_keymap_entry` in crate `libc`
   --> src/raw_stream.rs:518:32
    |
518 |         let mut keymap = libc::input_keymap_entry {
    |                                ^^^^^^^^^^^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_keymap_entry` in crate `libc`
   --> src/raw_stream.rs:531:32
    |
531 |         let mut keymap = libc::input_keymap_entry {
    |                                ^^^^^^^^^^^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_keymap_entry` in crate `libc`
   --> src/raw_stream.rs:555:32
    |
555 |         let mut keymap = libc::input_keymap_entry {
    |                                ^^^^^^^^^^^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_keymap_entry` in crate `libc`
   --> src/raw_stream.rs:574:32
    |
574 |         let mut keymap = libc::input_keymap_entry {
    |                                ^^^^^^^^^^^^^^^^^^ not found in `libc`

error[E0425]: cannot find value `KEY_CNT` in crate `libc`
  --> src/scancodes.rs:19:43
   |
19 |     pub(crate) const COUNT: usize = libc::KEY_CNT;
   |                                           ^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_absinfo` in crate `libc`
   --> src/sync_stream.rs:262:54
    |
262 |     pub fn get_abs_state(&self) -> io::Result<[libc::input_absinfo; AbsoluteAxisType::COUNT]> {
    |                                                      ^^^^^^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_event` in crate `libc`
   --> src/sync_stream.rs:414:51
    |
414 |                           let ev = InputEvent(libc::input_event {
    |                                                     ^^^^^^^^^^^ not found in `libc`
...
430 | /                 try_compensate!(
431 | |                     time,
432 | |                     start: Key,
433 | |                     KEY,
...   |
438 | |                     |vals, key| vals.contains(key)
439 | |                 );
    | |_________________- in this macro invocation
    |
    = note: this error originates in the macro `try_compensate` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0412]: cannot find type `input_absinfo` in crate `libc`
   --> src/sync_stream.rs:453:29
    |
453 |                     &[libc::input_absinfo],
    |                             ^^^^^^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_event` in crate `libc`
   --> src/sync_stream.rs:414:51
    |
414 |                           let ev = InputEvent(libc::input_event {
    |                                                     ^^^^^^^^^^^ not found in `libc`
...
447 | /                 try_compensate!(
448 | |                     time,
449 | |                     start: AbsoluteAxisType,
450 | |                     ABSOLUTE,
...   |
455 | |                     |vals, abs| vals[abs.0 as usize].value
456 | |                 );
    | |_________________- in this macro invocation
    |
    = note: this error originates in the macro `try_compensate` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0422]: cannot find struct, variant or union type `input_event` in crate `libc`
   --> src/sync_stream.rs:414:51
    |
414 |                           let ev = InputEvent(libc::input_event {
    |                                                     ^^^^^^^^^^^ not found in `libc`
...
464 | /                 try_compensate!(
465 | |                     time,
466 | |                     start: SwitchType,
467 | |                     SWITCH,
...   |
472 | |                     |vals, sw| vals.contains(sw)
473 | |                 );
    | |_________________- in this macro invocation
    |
    = note: this error originates in the macro `try_compensate` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0422]: cannot find struct, variant or union type `input_event` in crate `libc`
   --> src/sync_stream.rs:414:51
    |
414 |                           let ev = InputEvent(libc::input_event {
    |                                                     ^^^^^^^^^^^ not found in `libc`
...
481 | /                 try_compensate!(
482 | |                     time,
483 | |                     start: LedType,
484 | |                     LED,
...   |
489 | |                     |vals, led| vals.contains(led)
490 | |                 );
    | |_________________- in this macro invocation
    |
    = note: this error originates in the macro `try_compensate` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0422]: cannot find struct, variant or union/2679 type `input_event` in crate `libc`
   --> src/sync_stream.rs:491:43
    |
491 |                 let ev = InputEvent(libc::input_event {
    |                                           ^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_event` in crate `libc`
   --> src/sync_stream.rs:542:24
    |
542 |     event_buf: &[libc::input_event],
    |                        ^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_event` in crate `libc`
   --> src/sync_stream.rs:544:20
    |
544 | ) -> (Result<libc::input_event, bool>, Option<usize>) {
    |                    ^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_id` in crate `libc`
  --> src/uinput.rs:19:22
   |
19 |     id: Option<libc::input_id>,
   |                      ^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `uinput_setup` in crate `libc`
   --> src/uinput.rs:115:32
    |
115 |         let mut usetup = libc::uinput_setup {
    |                                ^^^^^^^^^^^^ not found in `libc`

error[E0425]: cannot find value `UINPUT_MAX_NAME_SIZE` in crate `libc`
   --> src/uinput.rs:117:29
    |
117 |             name: [0; libc::UINPUT_MAX_NAME_SIZE],
    |                             ^^^^^^^^^^^^^^^^^^^^ not found in `libc`

error[E0425]: cannot find value `UINPUT_MAX_NAME_SIZE` in crate `libc`
   --> src/uinput.rs:126:46
    |
126 |         assert!(name_bytes.len() + 1 < libc::UINPUT_MAX_NAME_SIZE);
    |                                              ^^^^^^^^^^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_id` in crate `libc`
   --> src/uinput.rs:133:25
    |
133 | const DEFAULT_ID: libc::input_id = libc::input_id {
    |                         ^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_id` in crate `libc`
   --> src/uinput.rs:133:42
    |
133 | const DEFAULT_ID: libc::input_id = libc::input_id {
    |                                          ^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `uinput_setup` in crate `libc`
   --> src/uinput.rs:146:39
    |
146 |     fn new(file: File, usetup: &libc::uinput_setup) -> io::Result<Self> {
    |                                       ^^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_event` in crate `libc`
   --> src/lib.rs:131:29
    |
131 | pub struct InputEvent(libc::input_event);
    |                             ^^^^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_event` in crate `libc`
   --> src/lib.rs:184:26
    |
184 |         InputEvent(libc::input_event {
    |                          ^^^^^^^^^^^ not found in `libc`

error[E0422]: cannot find struct, variant or union type `input_event` in crate `libc`
   --> src/lib.rs:202:26
    |
202 |         InputEvent(libc::input_event {
    |                          ^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_event` in crate `libc`
   --> src/lib.rs:211:17
    |
211 | impl From<libc::input_event> for InputEvent {
    |                 ^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_event` in crate `libc`
   --> src/lib.rs:212:24
    |
212 |     fn from(raw: libc::input_event) -> Self {
    |                        ^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_event` in crate `libc`
   --> src/lib.rs:217:18
    |
217 | impl AsRef<libc::input_event> for InputEvent {
    |                  ^^^^^^^^^^^ not found in `libc`

error[E0412]: cannot find type `input_event` in crate `libc`
   --> src/lib.rs:218:32
    |
218 |     fn as_ref(&self) -> &libc::input_event {
    |                                ^^^^^^^^^^^ not found in `libc`

error[E0282]: type annotations needed
  --> src/uinput.rs:35:17
   |
35 |             id: None,
   |                 ^^^^ cannot infer type for type parameter `T` declared on the enum `Option`

Some errors have detailed explanations: E0282, E0412, E0422, E0425, E0432.
For more information about an error, try `rustc --explain E0282`.
error: could not compile `evdev` due to 55 previous errors

We need an optional async (tokio) stream of events

Obviously this should be feature-gated since we don't want to import all of tokio for those who don't want it. But this crate is just begging for an async stream of InputEvent that can be driven by an async reactor instead of trying to drive it manually with epoll.

Support `EV_MSC` on uinput devices

As far as I can tell, evdev::uinput::VirtualDeviceBuilder doesn't provide any interface for advertising support for EV_MSC (as well as EV_REP, EV_LED, EV_SND, and EV_PWR) events. This makes it a little hard to make e.g. a virtual keyboard (many (maybe most?) keyboard drivers emit an EV_MSC MSC_SCAN event alongside each EV_KEY event) or a virtual proxy for a physical device (which might want to forward arbitrary events based on what the physical device supports).

busy wait to read events

I've noticed that the example for reading events is a busy wait. Device::events() (and Device::events_no_sync()) return a lot of empty iterators all the time. The result is that one cpu is constantly busy. How can I read events in a blocking way?

Btw, thanks for the nice library!

Move mouse to an absolute position.

I'm not entirely sure what I'm doing is how it is intended, but my goal is to set the mouse cursor to an absolute position on screen (not a relative position). I assumed I could do something like in the virtual joystick example, but my cursor does not move with that example.

Should I be creating different events?

Relative mouse movements did work. A workaround could be to compute the relative movement required to move to a certain absolute position, but that's kinda ugly.

Allow finer-grained permissions when opening a device

While programming beep-evdev, I realized that Device::open() and inside of it RawDevice always attempt to open with read and write permissions.
However, for my use case it would suffice to only have the append permission (and neither read nor write).

Is it possible to add a function to Device and RawDevice respectively, to allow to pass in pre-configured OpenOptions?
I'm thinking of something like open_with_options(path: impl AsRef<Path>, open_options: &OpenOptions).

Another possibility would be to implement From<std::fs::File> for Device and RawDevice respectively.
Then I could do:

let device: Device = OpenOptions::new()
    .append(true)
    .open(FILENAME)
    .into();

I'd be happy to draft a PR if this proposal is met with approval.

Couldn't get key from code: [MediaSelect, LaunchApp1, BrowserFavories, LaunchMail, etc..]

Hello and thank you for the wonderful crate.

I'm making a keyboard remapper and this crate has allowed me to do so. However there are a few special keys that this crate can' pick up. Namely [MediaSelect, LaunchApp1, BrowserFavories, LaunchMail, etc..].

When I grab a device, and I try to press any of those keys, it will print out errors of Couldn't get key from code: [...].
Is there a way that I can solve this issue? Thank you very much!

Release new version

Hi, as I'm about to release a tool and I use features of the current master branch, I'm highly interested in a new release of evdev.

Is there anything that has to be in, or done for, a new release?

Also, IIUC from #39, @coolreader18 has been enabled to publish a release to crates.io so emberian does not need to be involved?

Build failure on 32 bit arm

See seanyoung/cir#3 (comment) :

   Compiling evdev v0.12.0
error[E0308]: mismatched types
   --> /home/keshlamIR/.cargo/registry/src/github.com-1285ae84e5963aae/evdev-0.12.0/src/lib.rs:393:29
    |
393 |     let dur = Duration::new(tv.tv_sec.unsigned_abs(), tv.tv_usec as u32 * 1000);
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found `u32`
    |
help: you can convert a `u32` to a `u64`
    |
393 |     let dur = Duration::new(tv.tv_sec.unsigned_abs().into(), tv.tv_usec as u32 * 1000);
    |                                                     +++++++

On 32 bit arm time_t is an i32, see https://docs.rs/libc/latest/src/libc/unix/linux_like/linux/gnu/b64/mod.rs.html#20

VirtualDeviceBuilder does not support setting device properties

As far as I can tell, evdev::uinput::VirtualDeviceBuilder doesn't provide any interface for advertising support for Properties.

See section device properties: https://www.kernel.org/doc/Documentation/input/event-codes.txt
My tablet's pen buttons currently are not functioning correctly potentially due to missing #101 and the inability to set properties altough this might be xournalpp's fault (as it is sending the button events) it would be easier to add support for more device configuration here.

I also succeeded in doing this with unsafe rust but I don't want that in other people's repos.

If wanted I can make a pr for this

Why do `supported_...` methods return Option ?

I frequently find my self writing something like

device.supported_keys().unwrap_or(&AttributeSet::new())

which is kind of wired, because supported_... returns a collection so why does it return None if the collection is empty?

Even if I get the Some(&AttributeSetRef) variant no one guarantees me that the AttributeSet contains elements. So my code needs to handle the case where the AttributeSet is empty anyway. That leads me to the pattern from above.

From a user perspective, I can not think of any reason why I would ever do something with the None Variant. If I want to know if a device supports keys I would just call supported_events().

fetch_events() sometimes infinitely returns the same event

I have a simple event processing loop like this (println's inserted for debugging purposes):

match device.fetch_events() {
    Ok(events) => {
        for ev in events {
            println!("ev: {:?}", &ev);
            self.process_event(ev);
        }
        println!("done with events");
    },
    Err(e) => {
        eprintln!("E: error fetching events: {:?}", e);
        break;
    }
}

The only catch here is that process_event() can sometimes block for a second. Normally the output looks like this (it's a crappy gamepad and I don't even know what the noisy Z axis is):

ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 238574000 }, kind: AbsAxis(ABS_Z), value: 129 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 238574000 }, kind: Synchronization(SYN_REPORT), value: 0 }
done with events
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 242575000 }, kind: AbsAxis(ABS_Z), value: 130 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 242575000 }, kind: Synchronization(SYN_REPORT), value: 0 }
done with events
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 254574000 }, kind: AbsAxis(ABS_Z), value: 131 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 254574000 }, kind: Synchronization(SYN_REPORT), value: 0 }
done with events
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 276574000 }, kind: AbsAxis(ABS_Z), value: 132 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 276574000 }, kind: Synchronization(SYN_REPORT), value: 0 }
done with events
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 308573000 }, kind: Misc(MSC_SCAN), value: 589825 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 308573000 }, kind: Key(BTN_TRIGGER), value: 1 }
(blocking stuff happens here in response to BTN_TRIGGER)
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 308573000 }, kind: Synchronization(SYN_REPORT), value: 0 }
done with events
(these events were probably queued up during the blocking)
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 312576000 }, kind: AbsAxis(ABS_Z), value: 133 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 312576000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 316573000 }, kind: AbsAxis(ABS_Z), value: 131 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 316573000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 320570000 }, kind: AbsAxis(ABS_Z), value: 132 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 320570000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 342570000 }, kind: AbsAxis(ABS_Z), value: 131 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 342570000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 346573000 }, kind: AbsAxis(ABS_Z), value: 132 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 346573000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 358570000 }, kind: AbsAxis(ABS_Z), value: 131 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 358570000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 364573000 }, kind: AbsAxis(ABS_Z), value: 132 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 364573000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 394569000 }, kind: AbsAxis(ABS_Z), value: 131 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 394569000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 398571000 }, kind: AbsAxis(ABS_Z), value: 132 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 398571000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 402576000 }, kind: AbsAxis(ABS_Z), value: 133 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 402576000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 406569000 }, kind: AbsAxis(ABS_Z), value: 132 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 406569000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 424587000 }, kind: AbsAxis(ABS_Z), value: 133 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 424587000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 428579000 }, kind: AbsAxis(ABS_Z), value: 131 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 428579000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 432580000 }, kind: AbsAxis(ABS_Z), value: 132 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 432580000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 446590000 }, kind: AbsAxis(ABS_Z), value: 133 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 446590000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 450580000 }, kind: AbsAxis(ABS_Z), value: 132 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698763, tv_nsec: 450580000 }, kind: Synchronization(SYN_REPORT), value: 0 }
done with events

However if I press a lot of buttons while the loop is blocked, this happens fairly reliably:

ev: InputEvent { time: SystemTime { tv_sec: 1632698764, tv_nsec: 64575000 }, kind: AbsAxis(ABS_Z), value: 132 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698764, tv_nsec: 64575000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698764, tv_nsec: 76575000 }, kind: AbsAxis(ABS_Z), value: 131 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698764, tv_nsec: 76575000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698764, tv_nsec: 82575000 }, kind: AbsAxis(ABS_Z), value: 132 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698764, tv_nsec: 82575000 }, kind: Synchronization(SYN_REPORT), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698764, tv_nsec: 90577000 }, kind: Misc(MSC_SCAN), value: 589825 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698764, tv_nsec: 90577000 }, kind: Key(BTN_TRIGGER), value: 0 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698764, tv_nsec: 90577000 }, kind: Synchronization(SYN_REPORT), value: 0 }
done with events
done with events
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
ev: InputEvent { time: SystemTime { tv_sec: 1632698765, tv_nsec: 126532000 }, kind: AbsAxis(ABS_X), value: 127 }
(ad infinitum)

It looks like fetch_events first returns an empty iterator, and then one that just repeats the same event infinitely. Is this something that could be caused by just the hardware being crap (not even the nanosecond timestamp changes though), or could something be going wrong within the library?

From a quick look at the process with gdb it seems to be spending a lot of time within compensate_events but I'm not sure if that's a red herring.

Do you have any idea as to what might be causing this?

virtual keyboard example

Hi, I'm trying to figure out what this No such file or directory error message is trying to find?

This is my console output below. (i get same message as root as well)

user@gentoo evdev % cargo run --example virtual_keyboard
    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target/debug/examples/virtual_keyboard`
Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }

Unable to Device::open the mouse

If I try to open /dev/input/mouse0 I get 'Error: Os { code: 25, kind: Uncategorized, message: "Inappropriate ioctl for device" }'.
I'm not sure if this is a bug or if I'm missing something, but I can't find anything online.

(Doc Suggestion) Switch BTN_DPAD_UP to KEY_UP in Virtual Keyboard example

It's quite possible I'm the only person who would run into a problem with this, so feel free to ignore if that's the case.

It took me a little while debugging to realize that BTN_DPAD_UP and KEY_UP were not the same key; I had gotten the mouse example working and was playing around with the virtual keyboard example but couldn't figure out why nothing seemed to be happening or registering even though evtest showed the events coming through. My thought was "there must be a Linux event I need to throw to get the system to start paying attention to this device." When I started fiddling around more with the example, changing keys didn't seem to help much because I kept sticking to the BTN_ prefix when I swapped keys around.

Looking at evtest output for the normal keyboard and realizing that the event names didn't line up is what eventually clued me to the reason, and when I switched over to KEY_UP, KEY_(whatever) everything was working perfectly. But that only happened after I read through a bunch of Arch documentation trying to figure out if I needed to set a device property or something.

Again, it may just be me, feel free to ignore if you think this is more common knowledge, but if someone is coming in without the understanding that directional pad events can come through a keyboard device, and if their system isn't set up to respond to the directional pad events already, they might repeat my mistake and run the virtual keyboard code example and assume nothing is happening? If that's the case it might be clearer to use a KEY_ input instead of a BTN_DPAD input (but again, maybe that's something that's fairly standard knowledge).

Unexpected abs bits

When running evdev::enumerate I get the following output:

abs bits: 11110000000100000000000000000000000000000000
thread 'main' panicked at 'evdev: unexpected abs bits! report a bug'

I guess I was asked to open this issue ;)

As I don't know which exact infos you need, I'll just post my xinput output for now. Feel free to ask for additional information.

⎡ Virtual core pointer                    	id=2	[master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer              	id=4	[slave  pointer  (2)]
⎜   ↳ Logitech USB Receiver                   	id=9	[slave  pointer  (2)]
⎜   ↳ Logitech USB Receiver                   	id=10	[slave  pointer  (2)]
⎣ Virtual core keyboard                   	id=3	[master keyboard (2)]
    ↳ Virtual core XTEST keyboard             	id=5	[slave  keyboard (3)]
    ↳ Power Button                            	id=6	[slave  keyboard (3)]
    ↳ Power Button                            	id=7	[slave  keyboard (3)]
    ↳ Sleep Button                            	id=8	[slave  keyboard (3)]
    ↳ Burr-Brown from TI               USB Audio CODEC 	id=13	[slave  keyboard (3)]
    ↳ Logitech Gaming Keyboard G910           	id=11	[slave  keyboard (3)]
    ↳ Logitech Gaming Keyboard G910           	id=12	[slave  keyboard (3)]

evdev uses 100% of CPU

Evdev uses 100% of CPU because device open with O_NONBLOCK flag, so call to read returns early.

Consider alternative EventStream implementation

For a lot of drivers, grouping of events matters. The current EventStream implementation doesn't expose their grouping so it is difficult to properly implement functionalities using such devices if you also want to be async.

I have implemented an alternative API based on the same FetchEventsSynced struct used by the sync event interface. mattfbacon@7660fe9

I think this makes more sense because it provides flexibility based on the grouping. e.g., users can adapt it to a Stream by flattening the event groups, or process each chunk directly when it matters.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.