요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. _joystick-api:
=====================
Programming Interface
=====================
:Author: Ragnar Hojland Espinosa <ragnar@macula.net> - 7 Aug 1998
Introduction
============
.. important::
This document describes legacy ``js`` interface. Newer clients are
encouraged to switch to the generic event (``evdev``) interface.
The 1.0 driver uses a new, event based approach to the joystick driver.
Instead of the user program polling for the joystick values, the joystick
driver now reports only any changes of its state. See joystick-api.txt,
joystick.h and jstest.c included in the joystick package for more
information. The joystick device can be used in either blocking or
nonblocking mode, and supports select() calls.
For backward compatibility the old (v0.x) interface is still included.
Any call to the joystick driver using the old interface will return values
that are compatible to the old interface. This interface is still limited
to 2 axes, and applications using it usually decode only 2 buttons, although
the driver provides up to 32.
Initialization
==============
Open the joystick device following the usual semantics (that is, with open).
Since the driver now reports events instead of polling for changes,
immediately after the open it will issue a series of synthetic events
(JS_EVENT_INIT) that you can read to obtain the initial state of the
joystick.
By default, the device is opened in blocking mode::
int fd = open ("/dev/input/js0", O_RDONLY);
Event Reading
=============
::
struct js_event e;
read (fd, &e, sizeof(e));
where js_event is defined as::
struct js_event {
__u32 time; /* event timestamp in milliseconds */
__s16 value; /* value */
__u8 type; /* event type */
__u8 number; /* axis/button number */
};
If the read is successful, it will return sizeof(e), unless you wanted to read
more than one event per read as described in section 3.1.
js_event.type
-------------
The possible values of ``type`` are::
#define JS_EVENT_BUTTON 0x01 /* button pressed/released */
#define JS_EVENT_AXIS 0x02 /* joystick moved */
#define JS_EVENT_INIT 0x80 /* initial state of device */
As mentioned above, the driver will issue synthetic JS_EVENT_INIT ORed
events on open. That is, if it's issuing an INIT BUTTON event, the
current type value will be::
int type = JS_EVENT_BUTTON | JS_EVENT_INIT; /* 0x81 */
If you choose not to differentiate between synthetic or real events
you can turn off the JS_EVENT_INIT bits::
type &= ~JS_EVENT_INIT; /* 0x01 */
js_event.number
---------------
The values of ``number`` correspond to the axis or button that
generated the event. Note that they carry separate numeration (that
is, you have both an axis 0 and a button 0). Generally,
=============== =======
Axis number
=============== =======
1st Axis X 0
1st Axis Y 1
2nd Axis X 2
2nd Axis Y 3
...and so on
=============== =======
Hats vary from one joystick type to another. Some can be moved in 8
directions, some only in 4. The driver, however, always reports a hat as two
independent axes, even if the hardware doesn't allow independent movement.
js_event.value
--------------
For an axis, ``value`` is a signed integer between -32767 and +32767
representing the position of the joystick along that axis. If you
don't read a 0 when the joystick is ``dead``, or if it doesn't span the
full range, you should recalibrate it (with, for example, jscal).
For a button, ``value`` for a press button event is 1 and for a release
button event is 0.
Though this::
if (js_event.type == JS_EVENT_BUTTON) {
buttons_state ^= (1 << js_event.number);
}
may work well if you handle JS_EVENT_INIT events separately,
::
if ((js_event.type & ~JS_EVENT_INIT) == JS_EVENT_BUTTON) {
if (js_event.value)
buttons_state |= (1 << js_event.number);
else
buttons_state &= ~(1 << js_event.number);
}
is much safer since it can't lose sync with the driver. As you would
have to write a separate handler for JS_EVENT_INIT events in the first
snippet, this ends up being shorter.
js_event.time
-------------
The time an event was generated is stored in ``js_event.time``. It's a time
in milliseconds since ... well, since sometime in the past. This eases the
task of detecting double clicks, figuring out if movement of axis and button
presses happened at the same time, and similar.
Reading
=======
If you open the device in blocking mode, a read will block (that is,
wait) forever until an event is generated and effectively read. There
are two alternatives if you can't afford to wait forever (which is,
admittedly, a long time;)
a) use select to wait until there's data to be read on fd, or
until it timeouts. There's a good example on the select(2)
man page.
b) open the device in non-blocking mode (O_NONBLOCK)
O_NONBLOCK
----------
If read returns -1 when reading in O_NONBLOCK mode, this isn't
necessarily a "real" error (check errno(3)); it can just mean there
are no events pending to be read on the driver queue. You should read
all events on the queue (that is, until you get a -1).
For example,
::
while (1) {
while (read (fd, &e, sizeof(e)) > 0) {
process_event (e);
}
/* EAGAIN is returned when the queue is empty */
if (errno != EAGAIN) {
/* error */
}
/* do something interesting with processed events */
}
One reason for emptying the queue is that if it gets full you'll start
missing events since the queue is finite, and older events will get
overwritten.
The other reason is that you want to know all that happened, and not
delay the processing till later.
Why can the queue get full? Because you don't empty the queue as
mentioned, or because too much time elapses from one read to another
and too many events to store in the queue get generated. Note that
high system load may contribute to space those reads even more.
If time between reads is enough to fill the queue and lose an event,
the driver will switch to startup mode and next time you read it,
synthetic events (JS_EVENT_INIT) will be generated to inform you of
the actual state of the joystick.
.. note::
As of version 1.2.8, the queue is circular and able to hold 64
events. You can increment this size bumping up JS_BUFF_SIZE in
joystick.h and recompiling the driver.
In the above code, you might as well want to read more than one event
at a time using the typical read(2) functionality. For that, you would
replace the read above with something like::
struct js_event mybuffer[0xff];
int i = read (fd, mybuffer, sizeof(mybuffer));
In this case, read would return -1 if the queue was empty, or some
other value in which the number of events read would be i /
sizeof(js_event) Again, if the buffer was full, it's a good idea to
process the events and keep reading it until you empty the driver queue.
IOCTLs
======
The joystick driver defines the following ioctl(2) operations::
/* function 3rd arg */
#define JSIOCGAXES /* get number of axes char */
#define JSIOCGBUTTONS /* get number of buttons char */
#define JSIOCGVERSION /* get driver version int */
#define JSIOCGNAME(len) /* get identifier string char */
#define JSIOCSCORR /* set correction values &js_corr */
#define JSIOCGCORR /* get correction values &js_corr */
For example, to read the number of axes::
char number_of_axes;
ioctl (fd, JSIOCGAXES, &number_of_axes);
JSIOGCVERSION
-------------
JSIOGCVERSION is a good way to check in run-time whether the running
driver is 1.0+ and supports the event interface. If it is not, the
IOCTL will fail. For a compile-time decision, you can test the
JS_VERSION symbol::
#ifdef JS_VERSION
#if JS_VERSION > 0xsomething
JSIOCGNAME
----------
JSIOCGNAME(len) allows you to get the name string of the joystick - the same
as is being printed at boot time. The 'len' argument is the length of the
buffer provided by the application asking for the name. It is used to avoid
possible overrun should the name be too long::
char name[128];
if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0)
strscpy(name, "Unknown", sizeof(name));
printf("Name: %s\n", name);
JSIOC[SG]CORR
-------------
For usage on JSIOC[SG]CORR I suggest you to look into jscal.c They are
not needed in a normal program, only in joystick calibration software
such as jscal or kcmjoy. These IOCTLs and data types aren't considered
to be in the stable part of the API, and therefore may change without
warning in following releases of the driver.
Both JSIOCSCORR and JSIOCGCORR expect &js_corr to be able to hold
information for all axes. That is, struct js_corr corr[MAX_AXIS];
struct js_corr is defined as::
struct js_corr {
__s32 coef[8];
__u16 prec;
__u16 type;
};
and ``type``::
#define JS_CORR_NONE 0x00 /* returns raw values */
#define JS_CORR_BROKEN 0x01 /* broken line */
Backward compatibility
======================
The 0.x joystick driver API is quite limited and its usage is deprecated.
The driver offers backward compatibility, though. Here's a quick summary::
struct JS_DATA_TYPE js;
while (1) {
if (read (fd, &js, JS_RETURN) != JS_RETURN) {
/* error */
}
usleep (1000);
}
As you can figure out from the example, the read returns immediately,
with the actual state of the joystick::
struct JS_DATA_TYPE {
int buttons; /* immediate button state */
int x; /* immediate x axis value */
int y; /* immediate y axis value */
};
and JS_RETURN is defined as::
#define JS_RETURN sizeof(struct JS_DATA_TYPE)
To test the state of the buttons,
::
first_button_state = js.buttons & 1;
second_button_state = js.buttons & 2;
The axis values do not have a defined range in the original 0.x driver,
except that the values are non-negative. The 1.2.8+ drivers use a
fixed range for reporting the values, 1 being the minimum, 128 the
center, and 255 maximum value.
The v0.8.0.2 driver also had an interface for 'digital joysticks', (now
called Multisystem joysticks in this driver), under /dev/djsX. This driver
doesn't try to be compatible with that interface.
Final Notes
===========
::
____/| Comments, additions, and specially corrections are welcome.
\ o.O| Documentation valid for at least version 1.2.8 of the joystick
=(_)= driver and as usual, the ultimate source for documentation is
U to "Use The Source Luke" or, at your convenience, Vojtech ;)
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Legacy js interface와 초기화
1-42이 문서는 legacy `js` interface를 설명합니다. 새 client는 generic event interface인 `evdev`로 전환하도록 권장됩니다.
Joystick driver 1.0은 사용자 프로그램이 값을 polling하는 대신 상태가 바뀔 때만 event를 보고합니다. 추가 정보는 joystick package의 `joystick-api.txt`, `joystick.h`, `jstest.c`에 있으며 device는 blocking·nonblocking mode와 `select()`를 지원합니다.
하위 호환성을 위해 v0.x interface도 포함합니다. 옛 interface 호출에는 호환 값이 반환되지만 axis는 2개로 제한되고 application은 보통 button 2개만 decode합니다. Driver 자체는 최대 32개 button을 제공합니다.
일반적인 `open()` 의미로 `/dev/input/js0`을 엽니다. Driver는 polling 대신 event를 보고하므로 open 직후 joystick의 초기 상태를 얻을 수 있도록 합성 `JS_EVENT_INIT` event 연속열을 보냅니다. 기본 open은 `open("/dev/input/js0", O_RDONLY)`와 같은 blocking mode입니다.
권장 interface와 legacy 호환 범위를 구분합니다.
Open 직후 합성 event로 전체 초기 상태를 복원합니다.
.. _joystick-api:
=====================
Programming Interface
=====================
:Author: Ragnar Hojland Espinosa <ragnar@macula.net> - 7 Aug 1998
Introduction
============
.. important::
This document describes legacy ``js`` interface. Newer clients are
encouraged to switch to the generic event (``evdev``) interface.
The 1.0 driver uses a new, event based approach to the joystick driver.
Instead of the user program polling for the joystick values, the joystick
driver now reports only any changes of its state. See joystick-api.txt,
joystick.h and jstest.c included in the joystick package for more
information. The joystick device can be used in either blocking or
nonblocking mode, and supports select() calls.
For backward compatibility the old (v0.x) interface is still included.
Any call to the joystick driver using the old interface will return values
that are compatible to the old interface. This interface is still limited
to 2 axes, and applications using it usually decode only 2 buttons, although
the driver provides up to 32.
Initialization
==============
Open the joystick device following the usual semantics (that is, with open).
Since the driver now reports events instead of polling for changes,
immediately after the open it will issue a series of synthetic events
(JS_EVENT_INIT) that you can read to obtain the initial state of the
joystick.
By default, the device is opened in blocking mode::
int fd = open ("/dev/input/js0", O_RDONLY);
`js_event` 구조와 type·number
43-106Event 하나는 `struct js_event e`를 준비하고 `read(fd, &e, sizeof(e))`로 읽습니다. 단일 event read가 성공하면 여러 event를 요청한 경우가 아닌 한 `sizeof(e)`를 반환합니다.
Legacy joydev가 전달하는 고정 크기 event record입니다.
`type`은 button press·release인 `JS_EVENT_BUTTON`(0x01), joystick 이동인 `JS_EVENT_AXIS`(0x02), 초기 상태 표시인 `JS_EVENT_INIT`(0x80)을 사용합니다.
Open 때 합성 초기 event는 기본 type과 `JS_EVENT_INIT`을 OR합니다. 예를 들어 초기 button event는 `JS_EVENT_BUTTON | JS_EVENT_INIT`, 즉 0x81입니다. 합성과 실제 event를 구분하지 않으려면 `type &= ~JS_EVENT_INIT`으로 초기 flag를 지웁니다.
Base event와 초기 상태 flag는 bitwise OR로 결합됩니다.
`number`는 event를 만든 axis 또는 button 번호입니다. Axis와 button은 별도로 번호를 매기므로 axis 0과 button 0이 동시에 존재합니다. 일반적으로 첫 X·Y axis는 0·1, 두 번째 X·Y axis는 2·3입니다.
Hat의 물리 방향 수는 4방향 또는 8방향처럼 장치마다 다르지만 driver는 hardware가 독립 이동을 지원하지 않아도 항상 서로 독립적인 axis 두 개로 보고합니다.
Axis와 button 번호 공간은 서로 독립적입니다.
합성 여부와 base event 종류를 각각 해석합니다.
Event Reading
=============
::
struct js_event e;
read (fd, &e, sizeof(e));
where js_event is defined as::
struct js_event {
__u32 time; /* event timestamp in milliseconds */
__s16 value; /* value */
__u8 type; /* event type */
__u8 number; /* axis/button number */
};
If the read is successful, it will return sizeof(e), unless you wanted to read
more than one event per read as described in section 3.1.
js_event.type
-------------
The possible values of ``type`` are::
#define JS_EVENT_BUTTON 0x01 /* button pressed/released */
#define JS_EVENT_AXIS 0x02 /* joystick moved */
#define JS_EVENT_INIT 0x80 /* initial state of device */
As mentioned above, the driver will issue synthetic JS_EVENT_INIT ORed
events on open. That is, if it's issuing an INIT BUTTON event, the
current type value will be::
int type = JS_EVENT_BUTTON | JS_EVENT_INIT; /* 0x81 */
If you choose not to differentiate between synthetic or real events
you can turn off the JS_EVENT_INIT bits::
type &= ~JS_EVENT_INIT; /* 0x01 */
js_event.number
---------------
The values of ``number`` correspond to the axis or button that
generated the event. Note that they carry separate numeration (that
is, you have both an axis 0 and a button 0). Generally,
=============== =======
Axis number
=============== =======
1st Axis X 0
1st Axis Y 1
2nd Axis X 2
2nd Axis Y 3
...and so on
=============== =======
Hats vary from one joystick type to another. Some can be moved in 8
directions, some only in 4. The driver, however, always reports a hat as two
independent axes, even if the hardware doesn't allow independent movement.
`js_event.value`와 timestamp
107-148Axis event의 `value`는 -32767부터 +32767까지의 signed integer로 해당 axis의 joystick 위치를 나타냅니다. Joystick을 놓은 상태에서 0이 아니거나 전체 범위를 사용하지 못하면 `jscal` 같은 도구로 다시 calibration해야 합니다.
Button event의 `value`는 press 1, release 0입니다.
Button event마다 현재 bit를 XOR하는 방식은 `JS_EVENT_INIT`을 별도 처리할 때 동작할 수 있지만 driver와 상태 동기화를 잃을 수 있습니다. 더 안전한 방식은 INIT flag를 제거해 button event인지 검사하고 value가 non-zero면 bit를 OR, 0이면 bit를 clear하는 것입니다. 이 방식은 합성·실제 event 모두에서 절대 상태를 적용합니다.
Event 종류별 값 범위와 해석입니다.
Toggle 대신 event value가 말하는 절대 상태를 적용합니다.
`js_event.time`에는 event 생성 시각이 과거의 어떤 기준점부터 지난 millisecond로 저장됩니다. Double click 탐지, axis 이동과 button press가 동시에 일어났는지 판단하는 데 사용할 수 있습니다.
절대 wall-clock보다 event 사이의 시간 관계를 판단하는 값입니다.
js_event.value
--------------
For an axis, ``value`` is a signed integer between -32767 and +32767
representing the position of the joystick along that axis. If you
don't read a 0 when the joystick is ``dead``, or if it doesn't span the
full range, you should recalibrate it (with, for example, jscal).
For a button, ``value`` for a press button event is 1 and for a release
button event is 0.
Though this::
if (js_event.type == JS_EVENT_BUTTON) {
buttons_state ^= (1 << js_event.number);
}
may work well if you handle JS_EVENT_INIT events separately,
::
if ((js_event.type & ~JS_EVENT_INIT) == JS_EVENT_BUTTON) {
if (js_event.value)
buttons_state |= (1 << js_event.number);
else
buttons_state &= ~(1 << js_event.number);
}
is much safer since it can't lose sync with the driver. As you would
have to write a separate handler for JS_EVENT_INIT events in the first
snippet, this ends up being shorter.
js_event.time
-------------
The time an event was generated is stored in ``js_event.time``. It's a time
in milliseconds since ... well, since sometime in the past. This eases the
task of detecting double clicks, figuring out if movement of axis and button
presses happened at the same time, and similar.
Blocking·nonblocking read와 queue 복구
149-224Blocking mode에서 `read()`는 event가 생성되어 읽힐 때까지 계속 기다립니다. 무기한 기다릴 수 없다면 `select()`로 fd에 data가 생기거나 timeout될 때까지 기다리거나, device를 `O_NONBLOCK`으로 엽니다.
Nonblocking mode에서 `read()`가 -1을 반환해도 반드시 실제 오류는 아닙니다. `errno`가 `EAGAIN`이면 driver queue에 pending event가 없다는 뜻입니다. Queue가 빌 때까지 모든 event를 읽어야 합니다.
예제 loop는 `read()`가 양수인 동안 `process_event(e)`를 호출하고, queue가 비어 `EAGAIN`이 나오면 처리된 event를 이용해 다른 작업을 합니다. 다른 errno는 오류로 처리합니다.
Queue는 유한하므로 비우지 않아 가득 차면 오래된 event가 덮어써져 유실됩니다. Queue를 모두 비우면 발생한 일을 즉시 파악할 수 있고 처리를 나중으로 미루지 않습니다.
Read 간격이 너무 길거나 event가 너무 많이 생성되면 queue가 찰 수 있으며 system load가 read 간격을 더 벌릴 수 있습니다. Event가 유실되면 driver는 startup mode로 전환하고 다음 read 때 현재 joystick 상태를 알리는 합성 `JS_EVENT_INIT` event를 생성합니다.
Version 1.2.8 기준 queue는 64 event를 담는 circular buffer입니다. `joystick.h`의 `JS_BUFF_SIZE`를 늘리고 driver를 다시 compile하면 크기를 키울 수 있습니다.
대기 방식과 queue-empty 결과를 정리했습니다.
Queue를 매번 완전히 비워 overflow와 지연을 줄입니다.
유실이 발생하면 startup mode의 합성 상태로 다시 동기화합니다.
한 번에 여러 event를 읽으려면 `struct js_event mybuffer[0xff]` 같은 배열을 전달합니다. 반환값이 -1이면 queue가 비었고, 그 외에는 `i / sizeof(js_event)`가 읽은 event 수입니다. Buffer가 가득 찼다면 처리 후 driver queue가 빌 때까지 계속 읽는 것이 좋습니다.
Byte 반환값을 event 개수로 변환합니다.
Reading
=======
If you open the device in blocking mode, a read will block (that is,
wait) forever until an event is generated and effectively read. There
are two alternatives if you can't afford to wait forever (which is,
admittedly, a long time;)
a) use select to wait until there's data to be read on fd, or
until it timeouts. There's a good example on the select(2)
man page.
b) open the device in non-blocking mode (O_NONBLOCK)
O_NONBLOCK
----------
If read returns -1 when reading in O_NONBLOCK mode, this isn't
necessarily a "real" error (check errno(3)); it can just mean there
are no events pending to be read on the driver queue. You should read
all events on the queue (that is, until you get a -1).
For example,
::
while (1) {
while (read (fd, &e, sizeof(e)) > 0) {
process_event (e);
}
/* EAGAIN is returned when the queue is empty */
if (errno != EAGAIN) {
/* error */
}
/* do something interesting with processed events */
}
One reason for emptying the queue is that if it gets full you'll start
missing events since the queue is finite, and older events will get
overwritten.
The other reason is that you want to know all that happened, and not
delay the processing till later.
Why can the queue get full? Because you don't empty the queue as
mentioned, or because too much time elapses from one read to another
and too many events to store in the queue get generated. Note that
high system load may contribute to space those reads even more.
If time between reads is enough to fill the queue and lose an event,
the driver will switch to startup mode and next time you read it,
synthetic events (JS_EVENT_INIT) will be generated to inform you of
the actual state of the joystick.
.. note::
As of version 1.2.8, the queue is circular and able to hold 64
events. You can increment this size bumping up JS_BUFF_SIZE in
joystick.h and recompiling the driver.
In the above code, you might as well want to read more than one event
at a time using the typical read(2) functionality. For that, you would
replace the read above with something like::
struct js_event mybuffer[0xff];
int i = read (fd, mybuffer, sizeof(mybuffer));
In this case, read would return -1 if the queue was empty, or some
other value in which the number of events read would be i /
sizeof(js_event) Again, if the buffer was full, it's a good idea to
process the events and keep reading it until you empty the driver queue.
Joystick ioctl과 correction
225-295Joystick driver는 axis 수를 `char`로 얻는 `JSIOCGAXES`, button 수를 `char`로 얻는 `JSIOCGBUTTONS`, driver version을 `int`로 얻는 `JSIOCGVERSION`, 식별 문자열을 얻는 `JSIOCGNAME(len)`, correction 값을 설정·조회하는 `JSIOCSCORR`와 `JSIOCGCORR` ioctl을 정의합니다.
Operation과 세 번째 인자 형식입니다.
실행 중 driver가 1.0 이상이고 event interface를 지원하는지는 `JSIOCGVERSION`으로 검사할 수 있으며 지원하지 않으면 ioctl이 실패합니다. Compile-time에는 `JS_VERSION` symbol을 검사합니다. 원문 절 제목은 `JSIOGCVERSION`으로 표기하지만 operation 목록의 symbol은 `JSIOCGVERSION`입니다.
`JSIOCGNAME(len)`은 boot 때 출력되는 것과 같은 joystick 이름을 가져옵니다. `len`은 application buffer 길이여서 긴 이름으로 인한 overrun을 막습니다. Ioctl이 실패하면 예제는 `strscpy()`로 `Unknown`을 기록합니다.
`JSIOC[SG]CORR` 사용법은 `jscal.c`를 참고합니다. 일반 프로그램에는 필요 없고 `jscal`, `kcmjoy` 같은 calibration software만 사용합니다. 이 ioctl과 data type은 stable API로 간주되지 않아 경고 없이 바뀔 수 있습니다.
`JSIOCSCORR`와 `JSIOCGCORR`은 모든 axis 정보를 담을 수 있는 `struct js_corr corr[MAX_AXIS]`를 기대합니다. `struct js_corr`은 coefficient 여덟 개 `coef[8]`, precision `prec`, correction type `type`을 가집니다.
Axis correction parameter와 type 값입니다.
Open한 fd에서 장치 규모와 API 정보를 얻습니다.
IOCTLs
======
The joystick driver defines the following ioctl(2) operations::
/* function 3rd arg */
#define JSIOCGAXES /* get number of axes char */
#define JSIOCGBUTTONS /* get number of buttons char */
#define JSIOCGVERSION /* get driver version int */
#define JSIOCGNAME(len) /* get identifier string char */
#define JSIOCSCORR /* set correction values &js_corr */
#define JSIOCGCORR /* get correction values &js_corr */
For example, to read the number of axes::
char number_of_axes;
ioctl (fd, JSIOCGAXES, &number_of_axes);
JSIOGCVERSION
-------------
JSIOGCVERSION is a good way to check in run-time whether the running
driver is 1.0+ and supports the event interface. If it is not, the
IOCTL will fail. For a compile-time decision, you can test the
JS_VERSION symbol::
#ifdef JS_VERSION
#if JS_VERSION > 0xsomething
JSIOCGNAME
----------
JSIOCGNAME(len) allows you to get the name string of the joystick - the same
as is being printed at boot time. The 'len' argument is the length of the
buffer provided by the application asking for the name. It is used to avoid
possible overrun should the name be too long::
char name[128];
if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0)
strscpy(name, "Unknown", sizeof(name));
printf("Name: %s\n", name);
JSIOC[SG]CORR
-------------
For usage on JSIOC[SG]CORR I suggest you to look into jscal.c They are
not needed in a normal program, only in joystick calibration software
such as jscal or kcmjoy. These IOCTLs and data types aren't considered
to be in the stable part of the API, and therefore may change without
warning in following releases of the driver.
Both JSIOCSCORR and JSIOCGCORR expect &js_corr to be able to hold
information for all axes. That is, struct js_corr corr[MAX_AXIS];
struct js_corr is defined as::
struct js_corr {
__s32 coef[8];
__u16 prec;
__u16 type;
};
and ``type``::
#define JS_CORR_NONE 0x00 /* returns raw values */
#define JS_CORR_BROKEN 0x01 /* broken line */
v0.x backward compatibility
296-339Joystick driver v0.x API는 매우 제한적이며 deprecated지만 driver가 하위 호환성을 제공합니다. 옛 프로그램은 `struct JS_DATA_TYPE` 크기인 `JS_RETURN`만큼 read하고 짧은 `usleep(1000)`을 두며 현재 상태를 반복 조회합니다.
이 read는 즉시 반환합니다. `struct JS_DATA_TYPE`에는 현재 button bitmask인 `buttons`, 즉시 X axis 값인 `x`, Y axis 값인 `y`가 있으며 `JS_RETURN`은 `sizeof(struct JS_DATA_TYPE)`입니다.
Event가 아니라 read 시점의 즉시 상태를 반환합니다.
첫 button 상태는 `js.buttons & 1`, 두 번째는 `js.buttons & 2`로 검사합니다.
원래 v0.x driver에서 axis 값은 non-negative라는 점 외에 범위가 정의되지 않았습니다. 1.2.8 이상 driver는 최소 1, 중앙 128, 최대 255의 고정 범위를 사용합니다.
v0.8.0.2 driver는 현재 Multisystem joystick이라 부르는 digital joystick용 `/dev/djsX` interface도 제공했지만 현재 driver는 이 interface와 호환되지 않습니다.
v0.x 상태 polling과 v1.x event interface 차이입니다.
Deprecated API는 현재 상태 구조체를 반복해서 읽습니다.
Backward compatibility
======================
The 0.x joystick driver API is quite limited and its usage is deprecated.
The driver offers backward compatibility, though. Here's a quick summary::
struct JS_DATA_TYPE js;
while (1) {
if (read (fd, &js, JS_RETURN) != JS_RETURN) {
/* error */
}
usleep (1000);
}
As you can figure out from the example, the read returns immediately,
with the actual state of the joystick::
struct JS_DATA_TYPE {
int buttons; /* immediate button state */
int x; /* immediate x axis value */
int y; /* immediate y axis value */
};
and JS_RETURN is defined as::
#define JS_RETURN sizeof(struct JS_DATA_TYPE)
To test the state of the buttons,
::
first_button_state = js.buttons & 1;
second_button_state = js.buttons & 2;
The axis values do not have a defined range in the original 0.x driver,
except that the values are non-negative. The 1.2.8+ drivers use a
fixed range for reporting the values, 1 being the minimum, 128 the
center, and 255 maximum value.
The v0.8.0.2 driver also had an interface for 'digital joysticks', (now
called Multisystem joysticks in this driver), under /dev/djsX. This driver
doesn't try to be compatible with that interface.
적용 버전과 문서 기여
340-348의견, 추가 내용, 특히 수정 제안을 환영합니다. 이 문서는 joystick driver 1.2.8 이상에 유효하며 최종적인 정확성 판단에는 source code를 참고해야 합니다.
원문의 ASCII 서명을 내용 중심 note로 구조화했습니다.
문서와 실행 환경의 차이가 있을 때 확인할 순서입니다.
Final Notes
===========
::
____/| Comments, additions, and specially corrections are welcome.
\ o.O| Documentation valid for at least version 1.2.8 of the joystick
=(_)= driver and as usual, the ultimate source for documentation is
U to "Use The Source Luke" or, at your convenience, Vojtech ;)
요약·해설
joystick-api.rst:1-348새 client에는 evdev가 권장되지만, 이 문서는 `/dev/input/jsN`의 v1.x 변화 event와 v0.x polling 호환 형식을 정확히 다룹니다. Open 직후 초기 상태, event bit와 값, queue overflow 재동기화, capability·correction ioctl을 중심으로 읽어야 합니다.
Joydev client 구현의 핵심 단계입니다.
Open부터 초기화, queue drain과 event 적용까지의 안전한 경로입니다.