1 /* wiikeyboard.c: routines for dealing with the Wii USB keyboard
2    Copyright (c) 2008 Bjoern Giesler
3 
4    $Id: wiikeyboard.c 4109 2009-12-27 06:15:10Z fredm $
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 
20    Author contact information:
21 
22    E-mail: bjoern@giesler.de
23 
24 */
25 
26 #define _POSIX_THREADS
27 
28 #include <config.h>
29 
30 #include <stdio.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 
37 #include "display.h"
38 #include "fuse.h"
39 #include "keyboard.h"
40 #include "machine.h"
41 #include "settings.h"
42 #include "snapshot.h"
43 #include "spectrum.h"
44 #include "tape.h"
45 #include "ui/ui.h"
46 
47 #include <ogc/lwp.h>
48 #include <ogc/mutex.h>
49 #include <ogc/ipc.h>
50 
51 typedef struct {
52   u32 msgtype;
53   u32 unknown1;
54   u8 modifiers;
55   u8 unknown2;
56   u8 keys[6];
57 } wii_kbd_event;
58 
59 static s32 kbd = -1;
60 
61 static lwp_t kbdthread;
62 static mutex_t kbdmutex;
63 #define QUEUELEN 100
64 static input_event_t queue[QUEUELEN];
65 static int queuepos = 0;
66 static input_event_t releasequeue[QUEUELEN];
67 static int releasequeuepos = 0;
68 
69 static int
post_modifier(input_event_t * event,u8 old,u8 new,u8 modifier,input_key spectrum_key)70 post_modifier( input_event_t* event,
71 	       u8 old, u8 new, u8 modifier, input_key spectrum_key )
72 {
73   if( (old & modifier) && !(new & modifier) ) {
74     event->type = INPUT_EVENT_KEYRELEASE;
75     event->types.key.native_key = modifier;
76     event->types.key.spectrum_key = spectrum_key;
77     return 0;
78   } else if( !(old & modifier) && (new & modifier) ) {
79     event->type = INPUT_EVENT_KEYPRESS;
80     event->types.key.native_key = modifier;
81     event->types.key.spectrum_key = spectrum_key;
82     return 0;
83   }
84 
85   return 1;
86 }
87 
88 static void*
kbdthread_fn(void * arg)89 kbdthread_fn( void *arg )
90 {
91   wii_kbd_event event, oldevent;
92   int i, j, found;
93 
94   memset( &event, 0, sizeof(event) );
95   memset( &oldevent, 0, sizeof(oldevent) );
96 
97   while( kbd != -1 ) {
98     IOS_Ioctl( kbd, 0, NULL, 0, &event, sizeof(event) );
99 
100     /* skip connect (0) and disconnect (1) events */
101     if( event.msgtype != 2 ) continue;
102 
103     input_event_t fuse_event;
104 
105     LWP_MutexLock( kbdmutex );
106 
107 #define POSTMODIFIER(wiikey, speckey) \
108       if(queuepos < QUEUELEN) \
109 	if(post_modifier(queue + queuepos,		   \
110 			 oldevent.modifiers, event.modifiers,	\
111 			 wiikey, speckey) == 0) \
112 	  queuepos++
113 
114     POSTMODIFIER( 0x01, INPUT_KEY_Control_L );
115     POSTMODIFIER( 0x02, INPUT_KEY_Shift_L );
116     POSTMODIFIER( 0x04, INPUT_KEY_Alt_L );
117     POSTMODIFIER( 0x08, INPUT_KEY_Super_L );
118     POSTMODIFIER( 0x10, INPUT_KEY_Control_R );
119     POSTMODIFIER( 0x20, INPUT_KEY_Shift_R );
120     POSTMODIFIER( 0x40, INPUT_KEY_Alt_R );
121     POSTMODIFIER( 0x80, INPUT_KEY_Super_R );
122 
123 #undef POSTMODIFIER
124 
125     /* post keyreleases: old keys that have no new keys */
126     fuse_event.type = INPUT_EVENT_KEYRELEASE;
127     for(i=0; i<6; i++) {
128       if(oldevent.keys[i] == 0) break;
129       found = 0;
130       for( j=0; j<6; j++ ) {
131 	if(event.keys[j] == oldevent.keys[i]) {
132 	  found = 1;
133 	  break;
134 	}
135       }
136       if( !found ) {
137         fuse_event.types.key.spectrum_key = keysyms_remap(oldevent.keys[i]);
138         fuse_event.types.key.native_key = fuse_event.types.key.spectrum_key;
139 
140         if( queuepos >= QUEUELEN ) {
141 	  ui_error( UI_ERROR_WARNING, "%s: keyboard queue full", __func__ );
142 	  continue;
143 	}
144         queue[queuepos++] = fuse_event;
145         continue;
146       }
147     }
148 
149     fuse_event.type = INPUT_EVENT_KEYPRESS;
150     for( i=0; i<6; i++ ) {
151       if(event.keys[i] == 0) break;
152       found = 0;
153       for( j=0; j<6; j++ ) {
154         if(oldevent.keys[j] == event.keys[i]) {
155 	  found = 1;
156 	  break;
157 	}
158       }
159       if( !found ) {
160         fuse_event.types.key.spectrum_key = keysyms_remap( event.keys[i] );
161         fuse_event.types.key.native_key = fuse_event.types.key.spectrum_key;
162 
163         if( queuepos >= QUEUELEN ) {
164 	  ui_error( UI_ERROR_WARNING, "%s: keyboard queue full", __func__ );
165 	  continue;
166 	}
167         queue[queuepos++] = fuse_event;
168       }
169     }
170 
171     LWP_MutexUnlock( kbdmutex );
172     oldevent = event;
173   }
174 
175   return NULL;
176 }
177 
178 int
wiikeyboard_init(void)179 wiikeyboard_init( void )
180 {
181   kbd = IOS_Open( "/dev/usb/kbd", IPC_OPEN_RW );
182   if( kbd < 0 ) return kbd;
183 
184   if( LWP_MutexInit(&kbdmutex, 0) != 0 ||
185       LWP_CreateThread(&kbdthread, kbdthread_fn, NULL, NULL, 0, 80) != 0) {
186     IOS_Close(kbd);
187     return 1;
188   }
189 
190   return 0;
191 }
192 
wiikeyboard_end(void)193 int wiikeyboard_end(void)
194 {
195   if( kbd >= 0 ) IOS_Close(kbd);
196   kbd = -1;
197   return 0;
198 }
199 
200 void
keyboard_update(void)201 keyboard_update( void )
202 {
203   int i;
204 
205   for( i=0; i<releasequeuepos; i++ ) input_event(&(releasequeue[i]));
206   releasequeuepos = 0;
207 
208   LWP_MutexLock( kbdmutex );
209   for(i=0; i<queuepos; i++) {
210     if( queue[i].type == INPUT_EVENT_KEYRELEASE )
211       releasequeue[releasequeuepos++] = queue[i];
212     else
213       input_event( &(queue[i]) );
214   }
215   queuepos = 0;
216   LWP_MutexUnlock( kbdmutex );
217 }
218