added spaceball on linux
[dosdemo] / src / sdl / sball.c
1 #include "sball.h"
2
3 #ifdef __unix__
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <errno.h>
9 #include <unistd.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <sys/un.h>
13 #include <sys/select.h>
14
15 #define SPNAV_SOCK_PATH "/var/run/spnav.sock"
16 #define IS_OPEN         (sock != -1)
17
18 struct event_node {
19         sball_event event;
20         struct event_node *next;
21 };
22
23 /* only used for non-X mode, with spnav_remove_events */
24 static struct event_node *ev_queue, *ev_queue_tail;
25
26 /* AF_UNIX socket used for alternative communication with daemon */
27 static int sock = -1;
28
29 static int conn_unix(int s, const char *path)
30 {
31         struct sockaddr_un addr;
32
33         memset(&addr, 0, sizeof addr);
34         addr.sun_family = AF_UNIX;
35         strncpy(addr.sun_path, path, sizeof(addr.sun_path));
36
37         return connect(s, (struct sockaddr*)&addr, sizeof addr);
38 }
39
40
41 int sball_init(void)
42 {
43         int s;
44
45         if(IS_OPEN) {
46                 return -1;
47         }
48
49         if(!(ev_queue = malloc(sizeof *ev_queue))) {
50                 return -1;
51         }
52         ev_queue->next = 0;
53         ev_queue_tail = ev_queue;
54
55         if((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
56                 return -1;
57         }
58
59         if(conn_unix(s, SPNAV_SOCK_PATH) == -1) {
60                 perror("failed to connect");
61                 close(s);
62                 return -1;
63         }
64
65         sock = s;
66         return 0;
67 }
68
69 void sball_shutdown(void)
70 {
71         if(!IS_OPEN) {
72                 return;
73         }
74
75         if(sock != -1) {
76                 while(ev_queue) {
77                         void *tmp = ev_queue;
78                         ev_queue = ev_queue->next;
79                         free(tmp);
80                 }
81
82                 close(sock);
83                 sock = -1;
84         }
85 }
86
87 int sball_getdev(void)
88 {
89         return sock;
90 }
91
92 /* Checks both the event queue and the daemon socket for pending events.
93  * In either case, it returns immediately with true/false values (doesn't block).
94  */
95 int sball_pending(void)
96 {
97         fd_set rd_set;
98         struct timeval tv;
99
100         if(ev_queue->next) {
101                 return 1;
102         }
103
104         FD_ZERO(&rd_set);
105         FD_SET(sock, &rd_set);
106
107         /* don't block, just poll */
108         tv.tv_sec = tv.tv_usec = 0;
109
110         if(select(sock + 1, &rd_set, 0, 0, &tv) > 0) {
111                 return 1;
112         }
113         return 0;
114 }
115
116
117 /* If there are events waiting in the event queue, dequeue one and
118  * return that, otherwise read one from the daemon socket.
119  * This might block unless we called event_pending() first and it returned true.
120  */
121 static int read_event(int s, sball_event *event)
122 {
123         int i, rd;
124         int data[8];
125
126         /* if we have a queued event, deliver that one */
127         if(ev_queue->next) {
128                 struct event_node *node = ev_queue->next;
129                 ev_queue->next = ev_queue->next->next;
130
131                 /* dequeued the last event, must update tail pointer */
132                 if(ev_queue_tail == node) {
133                         ev_queue_tail = ev_queue;
134                 }
135
136                 memcpy(event, &node->event, sizeof *event);
137                 free(node);
138                 return event->type;
139         }
140
141         /* otherwise read one from the connection */
142         do {
143                 rd = read(s, data, sizeof data);
144         } while(rd == -1 && errno == EINTR);
145
146         if(rd <= 0) {
147                 return 0;
148         }
149
150         if(data[0] < 0 || data[0] > 2) {
151                 return 0;
152         }
153         event->type = data[0] ? SBALL_EV_BUTTON : SBALL_EV_MOTION;
154
155         if(event->type == SBALL_EV_MOTION) {
156                 for(i=0; i<6; i++) {
157                         event->motion.motion[i] = data[i + 1];
158                 }
159                 event->motion.motion[2] = -event->motion.motion[2];
160                 event->motion.motion[5] = -event->motion.motion[5];
161         } else {
162                 event->button.pressed = data[0] == 1 ? 1 : 0;
163                 event->button.id = data[1];
164         }
165
166         return event->type;
167 }
168
169
170 int sball_getevent(sball_event *ev)
171 {
172         if(sock) {
173                 if(read_event(sock, ev) > 0) {
174                         return ev->type;
175                 }
176         }
177         return 0;
178 }
179
180 #else   /* not __unix__ */
181
182 int sball_init(void)
183 {
184         return 0;
185 }
186
187 void sball_shutdown(void)
188 {
189 }
190
191 int sball_getdev(void)
192 {
193         return -1;
194 }
195
196 int sball_pending(void)
197 {
198         return 0;
199 }
200
201 int sball_getevent(sball_event *ev)
202 {
203         return SBALL_EV_NONE;
204 }
205
206 #endif