root/branches/dev/wmgui/main.c

Revision 116, 40.4 kB (checked in by dsmith, 2 years ago)

deprecated cwiid_{connect,disconnect,command}, added cwiid_{open,close,request_status,set_led,set_rumble,set_rpt_mode}

Line 
1 /* Copyright (C) 2007 L. Donnie Smith <cwiid@abstrakraft.org>
2  *
3  *  This program is free software; you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation; either version 2 of the License, or
6  *  (at your option) any later version.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
16  *
17  *  ChangeLog:
18  *  2007-05-16 L. Donnie Smith <cwiid@abstrakraft.org>
19  *  * changed cwiid_{connect,disconnect,command} to
20  *    cwiid_{open,close,request_status|set_led|set_rumble|set_rpt_mode}
21  *
22  *  2007-05-14 L. Donnie Smith <cwiid@abstrakraft.org>
23  *  * added timestamp to message callback
24  *  * use cwiid_get_acc_cal to get acc calibration values
25  *
26  *  2007-04-24 L. Donnie Smith <cwiid@abstrakraft.org>
27  *  * updated for API overhaul
28  *
29  *  2007-04-09 L. Donnie Smith <cwiid@abstrakraft.org>
30  *  * updated for libcwiid rename
31  *
32  *  2007-04-08 L. Donnie Smith <cwiid@anstrakraft.org>
33  *  * fixed signed/unsigned comparison warning in btnRead_clicked
34  *
35  *  2007-04-04 L. Donnie Smith <cwiid@abstrakraft.org>
36  *  * disconnect on cwiid_mesg_error
37  *
38  *  2007-04-03 L. Donnie Smith <cwiid@abstrakraft.org>
39  *  * commented custom cwiid_err (causing Xlib errors)
40  *
41  *  2007-03-01 L. Donnie Smith <cwiid@abstrakraft.org>
42  *  * Initial Changelog
43  *  * type audit (stdint, const, char booleans)
44  */
45
46 #define APP_NAME                "CWiid wmgui"
47 #define APP_COPYRIGHT   "Copyright (C) 2007 L. Donnie Smith " \
48                         "<cwiid@abstrakraft.org>"
49 #define APP_COMMENTS    "Wiimote GUI"
50
51 #include <ctype.h>
52 #include <float.h>
53 #include <math.h>
54 #include <stdio.h>
55 #include <stdint.h>
56 #include <string.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59
60 #include <glib.h>
61 #include <gtk/gtk.h>
62
63 #include "interface.h"
64 #include "support.h"
65
66 #include <bluetooth/bluetooth.h>
67 #include "cwiid.h"
68
69 #define PI      3.14159265358979323
70
71 struct stick {
72         char valid;
73         uint8_t x;
74         uint8_t y;
75         uint8_t max;
76 };
77
78 /* Globals */
79 cwiid_wiimote_t *wiimote = NULL;
80 bdaddr_t bdaddr;
81 struct acc_cal wm_cal, nc_cal;
82 struct cwiid_ir_mesg ir_data;
83 struct stick nc_stick;
84 struct stick cc_l_stick, cc_r_stick;
85
86 /* Widgets */
87 GtkWidget *winMain;
88 GtkWidget *winRW;
89 GtkWidget *winDialog;
90 GtkWidget *menuConnect, *menuDisconnect, *menuQuit, *menuRW, *menuAbout;
91 GtkWidget *chkAcc, *chkIR, *chkExt;
92 GtkWidget *chkLED1, *chkLED2, *chkLED3, *chkLED4;
93 GtkWidget *chkRumble;
94 GtkWidget *evUp, *evDown, *evLeft, *evRight, *evA, *evB,
95           *evMinus, *evPlus, *evHome, *ev1, *ev2;
96 GtkWidget *lblUp, *lblDown, *lblLeft, *lblRight, *lblA, *lblB,
97           *lblMinus, *lblPlus, *lblHome, *lbl1, *lbl2;
98 GtkWidget *lblAccX, *lblAccY, *lblAccZ;
99 GtkWidget *lblAccXVal, *lblAccYVal, *lblAccZVal;
100 GtkWidget *progAccX, *progAccY, *progAccZ;
101 GtkWidget *lblAcc, *lblRoll, *lblPitch;
102 GtkWidget *lblAccVal, *lblRollVal, *lblPitchVal;
103 GtkWidget *lblIR;
104 GtkWidget *drawIR;
105 GtkWidget *lblNC;
106 GtkWidget *drawNCStick;
107 GtkWidget *evNCC, *evNCZ;
108 GtkWidget *lblNCC, *lblNCZ;
109 GtkWidget *lblNCAccX, *lblNCAccY, *lblNCAccZ;
110 GtkWidget *lblNCAccXVal, *lblNCAccYVal, *lblNCAccZVal;
111 GtkWidget *progNCAccX, *progNCAccY, *progNCAccZ;
112 GtkWidget *lblNCAcc, *lblNCRoll, *lblNCPitch;
113 GtkWidget *lblNCAccVal, *lblNCRollVal, *lblNCPitchVal;
114 GtkWidget *evCCUp, *evCCDown, *evCCLeft, *evCCRight, *evCCMinus, *evCCPlus,
115           *evCCHome, *evCCA, *evCCB, *evCCX, *evCCY, *evCCZL, *evCCZR;
116 GtkWidget *lblCCUp, *lblCCDown, *lblCCLeft, *lblCCRight, *lblCCMinus,
117           *lblCCPlus, *lblCCHome, *lblCCA, *lblCCB, *lblCCX, *lblCCY, *lblCCZL,
118           *lblCCZR;
119 GtkWidget *drawCCLStick, *drawCCRStick;
120 GtkWidget *evCCL, *evCCR;
121 GtkWidget *lblCCL, *lblCCR;
122 GtkWidget *lblCCLVal, *lblCCRVal;
123 GtkWidget *progCCL, *progCCR;
124 GtkWidget *statConnection, *statBattery, *statExtension;
125 GtkWidget *txtReadOffset, *txtReadLen;
126 GtkWidget *radReadEEPROM, *radReadReg;
127 GtkWidget *btnRead;
128 GtkWidget *txtWriteOffset, *txtWriteData;
129 GtkWidget *radWriteEEPROM, *radWriteReg;
130 GtkWidget *btnWrite;
131 GtkWidget *tvRW;
132 GtkWidget *btnRWClose;
133 GtkWidget *btnBeep;
134
135 GtkTextBuffer *tbRW;
136
137 GdkColor btn_on, btn_off;
138
139 /* Utility functions */
140 void set_gui_state();
141 void clear_widgets();
142 void clear_acc_widgets();
143 void clear_ir_data();
144 void clear_nunchuk_widgets();
145 void clear_classic_widgets();
146 void message(GtkMessageType type, const gchar *message, GtkWindow *parent);
147 void status(const gchar *status);
148
149 /* GTK Callbacks */
150 gboolean winMain_delete_event(void);
151 gboolean winRW_delete_event(void);
152 void menuConnect_activate(void);
153 void menuDisconnect_activate(void);
154 void menuQuit_activate(void);
155 void menuRW_activate(void);
156 void menuAbout_activate(void);
157 void chkAcc_toggled(void);
158 void chkIR_toggled(void);
159 void chkExt_toggled(void);
160 void chkLED_toggled(void);
161 void chkRumble_toggled(void);
162 void drawIR_expose_event(void);
163 void drawStick_expose_event(GtkWidget *, GdkEventExpose *, struct stick *);
164 void btnRead_clicked(void);
165 void btnWrite_clicked(void);
166 void btnRWClose_clicked(void);
167 void btnBeep_clicked(void);
168
169 void set_report_mode(void);
170
171 /* Wiimote Callback */
172 cwiid_mesg_callback_t cwiid_callback;
173
174 /* Wiimote Handler Functions */
175 void cwiid_btn(struct cwiid_btn_mesg *);
176 void cwiid_acc(struct cwiid_acc_mesg *);
177 void cwiid_ir(struct cwiid_ir_mesg *);
178 void cwiid_nunchuk(struct cwiid_nunchuk_mesg *);
179 void cwiid_classic(struct cwiid_classic_mesg *);
180
181 /* GetOpt */
182 #define OPTSTRING       "h"
183 extern char *optarg;
184 extern int optind, opterr, optopt;
185
186 #define USAGE "usage:%s [-h] [BDADDR]\n"
187
188 /*
189 cwiid_err_t err;
190
191 void err(int id, const char *s, ...)
192 {
193         message(GTK_MESSAGE_ERROR, s, GTK_WINDOW(winMain));
194 }
195 */
196
197 int main (int argc, char *argv[])
198 {
199         int c;
200         char *str_addr;
201
202         gtk_set_locale ();
203         gtk_init (&argc, &argv);
204
205         if (!g_thread_supported()) {
206                 g_thread_init(NULL);
207         }
208         gdk_threads_init();
209         gdk_threads_enter();
210
211         /* cwiid_set_err(err); */
212
213         /* Parse Options */
214         while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
215                 switch (c) {
216                 case 'h':
217                         printf(USAGE, argv[0]);
218                         return 0;
219                         break;
220                 case '?':
221                         return -1;
222                         break;
223                 default:
224                         printf("unknown command-line option: -%c\n", c);
225                         break;
226                 }
227         }
228
229         /* BDADDR */
230         if (optind < argc) {
231                 if (str2ba(argv[optind], &bdaddr)) {
232                         printf("invalid bdaddr\n");
233                         bdaddr = *BDADDR_ANY;
234                 }
235                 optind++;
236                 if (optind < argc) {
237                         printf("invalid command-line\n");
238                         printf(USAGE, argv[0]);
239                         return -1;
240                 }
241         }
242         else if ((str_addr = getenv(WIIMOTE_BDADDR)) != NULL) {
243                 if (str2ba(str_addr, &bdaddr)) {
244                         printf("invalid address in %s\n", WIIMOTE_BDADDR);
245                         bdaddr = *BDADDR_ANY;
246                 }
247         }
248         else {
249                 bdaddr = *BDADDR_ANY;
250         }               
251
252         /* Create the window */
253         winMain = create_winMain();
254         winRW = create_winRW();
255
256         /* Lookup Widgets */
257         menuConnect = lookup_widget(winMain, "menuConnect");
258         menuDisconnect = lookup_widget(winMain, "menuDisconnect");
259         menuQuit = lookup_widget(winMain, "menuQuit");
260         menuRW = lookup_widget(winMain, "menuRW");
261         menuAbout = lookup_widget(winMain, "menuAbout");
262         chkAcc = lookup_widget(winMain, "chkAcc");
263         chkIR = lookup_widget(winMain, "chkIR");
264         chkExt = lookup_widget(winMain, "chkExt");
265         chkLED1 = lookup_widget(winMain, "chkLED1");
266         chkLED2 = lookup_widget(winMain, "chkLED2");
267         chkLED3 = lookup_widget(winMain, "chkLED3");
268         chkLED4 = lookup_widget(winMain, "chkLED4");
269         chkRumble = lookup_widget(winMain, "chkRumble");
270         evUp = lookup_widget(winMain, "evUp");
271         evDown = lookup_widget(winMain, "evDown");
272         evLeft = lookup_widget(winMain, "evLeft");
273         evRight = lookup_widget(winMain, "evRight");
274         evA = lookup_widget(winMain, "evA");
275         evB = lookup_widget(winMain, "evB");
276         evMinus = lookup_widget(winMain, "evMinus");
277         evPlus = lookup_widget(winMain, "evPlus");
278         evHome = lookup_widget(winMain, "evHome");
279         ev1 = lookup_widget(winMain, "ev1");
280         ev2 = lookup_widget(winMain, "ev2");
281         lblUp = lookup_widget(winMain, "lblUp");
282         lblDown = lookup_widget(winMain, "lblDown");
283         lblLeft = lookup_widget(winMain, "lblLeft");
284         lblRight = lookup_widget(winMain, "lblRight");
285         lblA = lookup_widget(winMain, "lblA");
286         lblB = lookup_widget(winMain, "lblB");
287         lblMinus = lookup_widget(winMain, "lblMinus");
288         lblPlus = lookup_widget(winMain, "lblPlus");
289         lblHome = lookup_widget(winMain, "lblHome");
290         lbl1 = lookup_widget(winMain, "lbl1");
291         lbl2 = lookup_widget(winMain, "lbl2");
292         lblAccX = lookup_widget(winMain, "lblAccX");
293         lblAccY = lookup_widget(winMain, "lblAccY");
294         lblAccZ = lookup_widget(winMain, "lblAccZ");
295         lblAccXVal = lookup_widget(winMain, "lblAccXVal");
296         lblAccYVal = lookup_widget(winMain, "lblAccYVal");
297         lblAccZVal = lookup_widget(winMain, "lblAccZVal");
298         progAccX = lookup_widget(winMain, "progAccX");
299         progAccY = lookup_widget(winMain, "progAccY");
300         progAccZ = lookup_widget(winMain, "progAccZ");
301         lblAcc = lookup_widget(winMain, "lblAcc");
302         lblRoll = lookup_widget(winMain, "lblRoll");
303         lblPitch = lookup_widget(winMain, "lblPitch");
304         lblAccVal = lookup_widget(winMain, "lblAccVal");
305         lblRollVal = lookup_widget(winMain, "lblRollVal");
306         lblPitchVal = lookup_widget(winMain, "lblPitchVal");
307         lblIR = lookup_widget(winMain, "lblIR");
308         drawIR = lookup_widget(winMain, "drawIR");
309         lblNC = lookup_widget(winMain, "lblNC");
310         drawNCStick = lookup_widget(winMain, "drawNCStick");
311         evNCC = lookup_widget(winMain, "evNCC");
312         evNCZ = lookup_widget(winMain, "evNCZ");
313         lblNCC = lookup_widget(winMain, "lblNCC");
314         lblNCZ = lookup_widget(winMain, "lblNCZ");
315         lblNCAccX = lookup_widget(winMain, "lblNCAccX");
316         lblNCAccY = lookup_widget(winMain, "lblNCAccY");
317         lblNCAccZ = lookup_widget(winMain, "lblNCAccZ");
318         lblNCAccXVal = lookup_widget(winMain, "lblNCAccXVal");
319         lblNCAccYVal = lookup_widget(winMain, "lblNCAccYVal");
320         lblNCAccZVal = lookup_widget(winMain, "lblNCAccZVal");
321         progNCAccX = lookup_widget(winMain, "progNCAccX");
322         progNCAccY = lookup_widget(winMain, "progNCAccY");
323         progNCAccZ = lookup_widget(winMain, "progNCAccZ");
324         lblNCAcc = lookup_widget(winMain, "lblNCAcc");
325         lblNCRoll = lookup_widget(winMain, "lblNCRoll");
326         lblNCPitch = lookup_widget(winMain, "lblNCPitch");
327         lblNCAccVal = lookup_widget(winMain, "lblNCAccVal");
328         lblNCRollVal = lookup_widget(winMain, "lblNCRollVal");
329         lblNCPitchVal = lookup_widget(winMain, "lblNCPitchVal");
330         evCCUp = lookup_widget(winMain, "evCCUp");
331         evCCDown = lookup_widget(winMain, "evCCDown");
332         evCCLeft = lookup_widget(winMain, "evCCLeft");
333         evCCRight = lookup_widget(winMain, "evCCRight");
334         evCCMinus = lookup_widget(winMain, "evCCMinus");
335         evCCPlus = lookup_widget(winMain, "evCCPlus");
336         evCCHome = lookup_widget(winMain, "evCCHome");
337         evCCA = lookup_widget(winMain, "evCCA");
338         evCCB = lookup_widget(winMain, "evCCB");
339         evCCX = lookup_widget(winMain, "evCCX");
340         evCCY = lookup_widget(winMain, "evCCY");
341         evCCZL = lookup_widget(winMain, "evCCZL");
342         evCCZR = lookup_widget(winMain, "evCCZR");
343         lblCCUp = lookup_widget(winMain, "lblCCUp");
344         lblCCDown = lookup_widget(winMain, "lblCCDown");
345         lblCCLeft = lookup_widget(winMain, "lblCCLeft");
346         lblCCRight = lookup_widget(winMain, "lblCCRight");
347         lblCCMinus = lookup_widget(winMain, "lblCCMinus");
348         lblCCPlus = lookup_widget(winMain, "lblCCPlus");
349         lblCCHome = lookup_widget(winMain, "lblCCHome");
350         lblCCA = lookup_widget(winMain, "lblCCA");
351         lblCCB = lookup_widget(winMain, "lblCCB");
352         lblCCX = lookup_widget(winMain, "lblCCX");
353         lblCCY = lookup_widget(winMain, "lblCCY");
354         lblCCZL = lookup_widget(winMain, "lblCCZL");
355         lblCCZR = lookup_widget(winMain, "lblCCZR");
356         drawCCLStick = lookup_widget(winMain, "drawCCLStick");
357         drawCCRStick = lookup_widget(winMain, "drawCCRStick");
358         evCCL = lookup_widget(winMain, "evCCL");
359         evCCR = lookup_widget(winMain, "evCCR");
360         lblCCL = lookup_widget(winMain, "lblCCL");
361         lblCCR = lookup_widget(winMain, "lblCCR");
362         lblCCLVal = lookup_widget(winMain, "lblCCLVal");
363         lblCCRVal = lookup_widget(winMain, "lblCCRVal");
364         progCCL = lookup_widget(winMain, "progCCL");
365         progCCR = lookup_widget(winMain, "progCCR");
366         statConnection = lookup_widget(winMain, "statConnection");
367         statBattery = lookup_widget(winMain, "statBattery");
368         statExtension = lookup_widget(winMain, "statExtension");
369         txtReadOffset = lookup_widget(winRW, "txtReadOffset");
370         txtReadLen = lookup_widget(winRW, "txtReadLen");
371         radReadEEPROM = lookup_widget(winRW, "radReadEEPROM");
372         radReadReg = lookup_widget(winRW, "radReadReg");
373         btnRead = lookup_widget(winRW, "btnRead");
374         txtWriteOffset = lookup_widget(winRW, "txtWriteOffset");
375         txtWriteData = lookup_widget(winRW, "txtWriteData");
376         radWriteEEPROM = lookup_widget(winRW, "radWriteEEPROM");
377         radWriteReg = lookup_widget(winRW, "radWriteReg");
378         btnWrite = lookup_widget(winRW, "btnWrite");
379         tvRW = lookup_widget(winRW, "tvRW");
380         btnRWClose = lookup_widget(winRW, "btnRWClose");
381         btnBeep = lookup_widget(winMain, "btnBeep");
382        
383         tbRW = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tvRW));
384
385         /* Connect Callbacks */
386         g_signal_connect(winMain, "delete_event", G_CALLBACK(winMain_delete_event),
387                          NULL);
388         g_signal_connect(winRW, "delete_event", G_CALLBACK(winRW_delete_event),
389                          NULL);
390         g_signal_connect(menuConnect, "activate", G_CALLBACK(menuConnect_activate),
391                          NULL);
392         g_signal_connect(menuDisconnect, "activate",
393                          G_CALLBACK(menuDisconnect_activate), NULL);
394         g_signal_connect(menuQuit, "activate", G_CALLBACK(menuQuit_activate),
395                          NULL);
396         g_signal_connect(menuRW, "activate", G_CALLBACK(menuRW_activate), NULL);
397         g_signal_connect(menuAbout, "activate", G_CALLBACK(menuAbout_activate),
398                          NULL);
399         g_signal_connect(chkAcc, "toggled", G_CALLBACK(chkAcc_toggled), NULL);
400         g_signal_connect(chkIR, "toggled", G_CALLBACK(chkIR_toggled), NULL);
401         g_signal_connect(chkExt, "toggled", G_CALLBACK(chkExt_toggled), NULL);
402         g_signal_connect(chkLED1, "toggled", G_CALLBACK(chkLED_toggled), NULL);
403         g_signal_connect(chkLED2, "toggled", G_CALLBACK(chkLED_toggled), NULL);
404         g_signal_connect(chkLED3, "toggled", G_CALLBACK(chkLED_toggled), NULL);
405         g_signal_connect(chkLED4, "toggled", G_CALLBACK(chkLED_toggled), NULL);
406         g_signal_connect(chkRumble, "toggled", G_CALLBACK(chkRumble_toggled),
407                          NULL);
408         g_signal_connect(drawIR, "expose_event",
409                          G_CALLBACK(drawIR_expose_event), NULL);
410         g_signal_connect(drawNCStick, "expose_event",
411                          G_CALLBACK(drawStick_expose_event), &nc_stick);
412         g_signal_connect(drawCCLStick, "expose_event",
413                          G_CALLBACK(drawStick_expose_event), &cc_l_stick);
414         g_signal_connect(drawCCRStick, "expose_event",
415                          G_CALLBACK(drawStick_expose_event), &cc_r_stick);
416         g_signal_connect(btnRead, "clicked", G_CALLBACK(btnRead_clicked), NULL);
417         g_signal_connect(btnWrite, "clicked", G_CALLBACK(btnWrite_clicked), NULL);
418         g_signal_connect(btnRWClose, "clicked", G_CALLBACK(btnRWClose_clicked),
419                          NULL);
420         g_signal_connect(btnBeep, "clicked", G_CALLBACK(btnBeep_clicked), NULL);
421
422         /* Initialize */
423         btn_on.red = 0; btn_on.blue = 0; btn_on.green = 0xFFFF;
424         btn_off = gtk_widget_get_style(evUp)->bg[GTK_STATE_NORMAL];
425
426         nc_stick.max = 0xFF;
427         cc_l_stick.max = CWIID_CLASSIC_L_STICK_MAX;
428         cc_r_stick.max = CWIID_CLASSIC_R_STICK_MAX;
429
430         set_gui_state();
431         clear_widgets();
432         status("No connection");
433
434         gtk_widget_show(winMain);
435
436         gtk_main();
437         gdk_threads_leave();
438         return 0;
439 }
440
441 void message(GtkMessageType type, const gchar *message, GtkWindow *parent)
442 {
443         GtkWidget *dialog;
444
445         dialog = gtk_message_dialog_new(parent, 0, type, GTK_BUTTONS_OK, message);
446         gtk_dialog_run(GTK_DIALOG(dialog));
447         gtk_widget_destroy(dialog);
448 }
449
450 void status(const gchar *status)
451 {
452         gtk_statusbar_push(GTK_STATUSBAR(statConnection), 0, status);
453 }
454
455 void set_gui_state()
456 {
457         gboolean connected;
458         gboolean acc_active;
459         gboolean ext_active;
460
461         connected = wiimote ? TRUE : FALSE;
462         /* Set Input Widget Sensitivities */
463         gtk_widget_set_sensitive(menuConnect, !connected);
464         gtk_widget_set_sensitive(menuDisconnect, connected);
465         gtk_widget_set_sensitive(chkLED1, connected);
466         gtk_widget_set_sensitive(chkLED2, connected);
467         gtk_widget_set_sensitive(chkLED3, connected);
468         gtk_widget_set_sensitive(chkLED4, connected);
469         gtk_widget_set_sensitive(chkRumble, connected);
470         gtk_widget_set_sensitive(btnRead, connected);
471         gtk_widget_set_sensitive(btnWrite, connected);
472
473         /* Set Button Sensitivities */
474         gtk_widget_set_sensitive(lblUp, connected);
475         gtk_widget_set_sensitive(lblDown, connected);
476         gtk_widget_set_sensitive(lblLeft, connected);
477         gtk_widget_set_sensitive(lblRight, connected);
478         gtk_widget_set_sensitive(lblA, connected);
479         gtk_widget_set_sensitive(lblB, connected);
480         gtk_widget_set_sensitive(lblMinus, connected);
481         gtk_widget_set_sensitive(lblPlus, connected);
482         gtk_widget_set_sensitive(lblHome, connected);
483         gtk_widget_set_sensitive(lbl1, connected);
484         gtk_widget_set_sensitive(lbl2, connected);
485
486         acc_active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkAcc));
487         /* Set Acc Widget Sensitivities */
488         gtk_widget_set_sensitive(lblAccX, acc_active);
489         gtk_widget_set_sensitive(lblAccY, acc_active);
490         gtk_widget_set_sensitive(lblAccZ, acc_active);
491         gtk_widget_set_sensitive(lblAccXVal, acc_active);
492         gtk_widget_set_sensitive(lblAccYVal, acc_active);
493         gtk_widget_set_sensitive(lblAccZVal, acc_active);
494         gtk_widget_set_sensitive(progAccX, acc_active);
495         gtk_widget_set_sensitive(progAccY, acc_active);
496         gtk_widget_set_sensitive(progAccZ, acc_active);
497         gtk_widget_set_sensitive(lblAcc, acc_active);
498         gtk_widget_set_sensitive(lblRoll, acc_active);
499         gtk_widget_set_sensitive(lblPitch, acc_active);
500         gtk_widget_set_sensitive(lblAccVal, acc_active);
501         gtk_widget_set_sensitive(lblRollVal, acc_active);
502         gtk_widget_set_sensitive(lblPitchVal, acc_active);
503
504         /* Set IC Widget Sensitivities */
505         gtk_widget_set_sensitive(lblIR,
506       gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkIR)));
507
508         ext_active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkExt));
509         /* Set Extension Widget Sensitivities */
510         gtk_widget_set_sensitive(lblNCC, ext_active);
511         gtk_widget_set_sensitive(lblNCZ, ext_active);
512         gtk_widget_set_sensitive(lblNCAccX, ext_active);
513         gtk_widget_set_sensitive(lblNCAccY, ext_active);
514         gtk_widget_set_sensitive(lblNCAccZ, ext_active);
515         gtk_widget_set_sensitive(lblNCAccXVal, ext_active);
516         gtk_widget_set_sensitive(lblNCAccYVal, ext_active);
517         gtk_widget_set_sensitive(lblNCAccZVal, ext_active);
518         gtk_widget_set_sensitive(progNCAccX, ext_active);
519         gtk_widget_set_sensitive(progNCAccY, ext_active);
520         gtk_widget_set_sensitive(progNCAccZ, ext_active);
521         gtk_widget_set_sensitive(lblNCAcc, ext_active);
522         gtk_widget_set_sensitive(lblNCRoll, ext_active);
523         gtk_widget_set_sensitive(lblNCPitch, ext_active);
524         gtk_widget_set_sensitive(lblNCAccVal, ext_active);
525         gtk_widget_set_sensitive(lblNCRollVal, ext_active);
526         gtk_widget_set_sensitive(lblNCPitchVal, ext_active);
527         gtk_widget_set_sensitive(lblCCUp, ext_active);
528         gtk_widget_set_sensitive(lblCCDown, ext_active);
529         gtk_widget_set_sensitive(lblCCLeft, ext_active);
530         gtk_widget_set_sensitive(lblCCRight, ext_active);
531         gtk_widget_set_sensitive(lblCCMinus, ext_active);
532         gtk_widget_set_sensitive(lblCCPlus, ext_active);
533         gtk_widget_set_sensitive(lblCCHome, ext_active);
534         gtk_widget_set_sensitive(lblCCA, ext_active);
535         gtk_widget_set_sensitive(lblCCB, ext_active);
536         gtk_widget_set_sensitive(lblCCX, ext_active);
537         gtk_widget_set_sensitive(lblCCY, ext_active);
538         gtk_widget_set_sensitive(lblCCZL, ext_active);
539         gtk_widget_set_sensitive(lblCCZR, ext_active);
540         gtk_widget_set_sensitive(lblCCL, ext_active);
541         gtk_widget_set_sensitive(lblCCR, ext_active);
542         gtk_widget_set_sensitive(lblCCLVal, ext_active);
543         gtk_widget_set_sensitive(lblCCRVal, ext_active);
544         gtk_widget_set_sensitive(progCCL, ext_active);
545         gtk_widget_set_sensitive(progCCR, ext_active);
546 }
547
548 void clear_widgets()
549 {
550         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(chkLED1), FALSE);
551         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(chkLED2), FALSE);
552         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(chkLED3), FALSE);
553         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(chkLED4), FALSE);
554         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(chkRumble), FALSE);
555
556         gtk_statusbar_push(GTK_STATUSBAR(statBattery), 0, "");
557         gtk_statusbar_push(GTK_STATUSBAR(statExtension), 0, "");
558
559         clear_acc_widgets();
560         clear_ir_data();
561         clear_nunchuk_widgets();
562         clear_classic_widgets();
563 }
564
565 void clear_acc_widgets()
566 {
567         gtk_label_set_text(GTK_LABEL(lblAccXVal), "0");
568         gtk_label_set_text(GTK_LABEL(lblAccYVal), "0");
569         gtk_label_set_text(GTK_LABEL(lblAccZVal), "0");
570         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progAccX), 0.0);
571         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progAccY), 0.0);
572         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progAccZ), 0.0);
573         gtk_label_set_text(GTK_LABEL(lblAccVal), "0");
574         gtk_label_set_text(GTK_LABEL(lblRollVal), "0");
575         gtk_label_set_text(GTK_LABEL(lblPitchVal), "0");
576 }
577
578 void clear_ir_data()
579 {
580         int i;
581
582         for (i=0; i < CWIID_IR_SRC_COUNT; i++) {
583                 ir_data.src[i].pos[CWIID_X] = -1;
584                 ir_data.src[i].pos[CWIID_Y] = -1;
585                 ir_data.src[i].size = -1;
586         }
587         gtk_widget_queue_draw(drawIR);
588 }
589
590 void clear_nunchuk_widgets()
591 {
592         nc_stick.valid = 0;
593         gtk_widget_queue_draw(drawNCStick);
594
595         gtk_label_set_text(GTK_LABEL(lblNCAccXVal), "0");
596         gtk_label_set_text(GTK_LABEL(lblNCAccYVal), "0");
597         gtk_label_set_text(GTK_LABEL(lblNCAccZVal), "0");
598         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progNCAccX), 0.0);
599         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progNCAccY), 0.0);
600         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progNCAccZ), 0.0);
601         gtk_label_set_text(GTK_LABEL(lblNCAccVal), "0");
602         gtk_label_set_text(GTK_LABEL(lblNCRollVal), "0");
603         gtk_label_set_text(GTK_LABEL(lblNCPitchVal), "0");
604 }
605
606 void clear_classic_widgets()
607 {
608         cc_l_stick.valid = 0;
609         gtk_widget_queue_draw(drawCCLStick);
610         cc_r_stick.valid = 0;
611         gtk_widget_queue_draw(drawCCRStick);
612
613         gtk_label_set_text(GTK_LABEL(lblCCLVal), "0");
614         gtk_label_set_text(GTK_LABEL(lblCCRVal), "0");
615         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progCCL), 0.0);
616         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progCCR), 0.0);
617 }
618
619 gboolean winMain_delete_event(void)
620 {
621         menuQuit_activate();
622         return FALSE;
623 }
624
625 gboolean winRW_delete_event(void)
626 {
627         btnRWClose_clicked();
628         return TRUE;
629 }
630
631 void menuConnect_activate(void)
632 {
633         char reset_bdaddr = 0;
634
635         if (bacmp(&bdaddr, BDADDR_ANY) == 0) {
636                 reset_bdaddr = 1;
637         }
638         message(GTK_MESSAGE_INFO,
639                 "Put Wiimote in discoverable mode (press 1+2) and press OK",
640                  GTK_WINDOW(winMain));
641         if ((wiimote = cwiid_open(&bdaddr, CWIID_FLAG_MESG_IFC)) == NULL) {
642                 message(GTK_MESSAGE_ERROR, "Unable to connect", GTK_WINDOW(winMain));
643                 status("No connection");
644         }
645         else if (cwiid_set_mesg_callback(wiimote, &cwiid_callback)) {
646                 message(GTK_MESSAGE_ERROR, "Error setting callback",
647                         GTK_WINDOW(winMain));
648                 if (cwiid_close(wiimote)) {
649                         message(GTK_MESSAGE_ERROR, "Error on disconnect",
650                                 GTK_WINDOW(winMain));
651                 }
652                 wiimote = NULL;
653                 status("No connection");
654         }
655         else {
656                 status("Connected");
657                 if (cwiid_get_acc_cal(wiimote, CWIID_EXT_NONE, &wm_cal)) {
658                         message(GTK_MESSAGE_ERROR, "Unable to retrieve accelerometer "
659                                 "calibration", GTK_WINDOW(winMain));
660                 }
661                 set_gui_state();
662                 set_report_mode();
663                 cwiid_request_status(wiimote);
664         }
665
666         if (reset_bdaddr) {
667                 bdaddr = *BDADDR_ANY;
668         }
669 }
670
671 void menuDisconnect_activate(void)
672 {
673         if (cwiid_close(wiimote)) {
674                 message(GTK_MESSAGE_ERROR, "Error on disconnect", GTK_WINDOW(winMain));
675         }
676         wiimote = NULL;
677         status("No connection");
678         clear_widgets();
679         set_gui_state();
680 }
681
682 void menuQuit_activate(void)
683 {
684         if (wiimote) {
685                 menuDisconnect_activate();
686         }
687         gtk_main_quit();
688 }
689
690 void menuRW_activate(void)
691 {
692         gtk_widget_show(winRW);
693 }
694
695 void menuAbout_activate(void)
696 {
697         gtk_show_about_dialog(GTK_WINDOW(winMain),
698                               "name", APP_NAME,
699                               "version", CWIID_VERSION,
700                               "copyright", APP_COPYRIGHT,
701                               "comments", APP_COMMENTS,
702                               NULL);
703 }
704
705 void chkAcc_toggled(void)
706 {
707         if (wiimote) {
708                 set_report_mode();
709         }
710         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkAcc))) {
711                 clear_acc_widgets();
712         }
713         set_gui_state();
714 }
715
716 void chkIR_toggled(void)
717 {
718         if (wiimote) {
719                 set_report_mode();
720         }
721         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkIR))) {
722                 clear_ir_data();
723         }
724         set_gui_state();
725 }
726
727 void chkExt_toggled(void)
728 {
729         if (wiimote) {
730                 set_report_mode();
731         }
732         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkExt))) {
733                 clear_nunchuk_widgets();
734                 clear_classic_widgets();
735         }
736         set_gui_state();
737 }
738
739 void chkLED_toggled(void)
740 {
741         uint8_t LED_state;
742
743         if (wiimote) {
744                 LED_state =
745                   (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkLED1))
746                     ? CWIID_LED1_ON : 0) |
747                   (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkLED2))
748                     ? CWIID_LED2_ON : 0) |
749                   (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkLED3))
750                     ? CWIID_LED3_ON : 0) |
751                   (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkLED4))
752                     ? CWIID_LED4_ON : 0);
753                 if (cwiid_set_led(wiimote, LED_state)) {
754                         message(GTK_MESSAGE_ERROR, "error setting LEDs",
755                                 GTK_WINDOW(winMain));
756                 }
757         }
758 }
759
760 void chkRumble_toggled(void)
761 {
762         if (wiimote) {
763                 if (cwiid_set_rumble(wiimote,
764                   gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkRumble)))) {
765                         message(GTK_MESSAGE_ERROR, "error setting rumble",
766                                 GTK_WINDOW(winMain));
767                 }
768         }
769 }
770
771 void drawIR_expose_event(void)
772 {
773         int i;
774         int size;
775         gint width, height;
776
777         gdk_window_get_geometry(drawIR->window, NULL, NULL, &width, &height, NULL);
778
779         for (i=0; i < CWIID_IR_SRC_COUNT; i++) {
780                 if (ir_data.src[i].valid) {
781                         if (ir_data.src[i].size == -1) {
782                                 size = 3;
783                         }
784                         else {
785                                 size = ir_data.src[i].size+1;
786                         }
787                         gdk_draw_arc(drawIR->window,
788                                      drawIR->style->fg_gc[GTK_WIDGET_STATE(drawIR)],
789                                      TRUE,
790                                                  ir_data.src[i].pos[CWIID_X] * width / CWIID_IR_X_MAX,
791                                                  height - ir_data.src[i].pos[CWIID_Y] * height /
792                                               CWIID_IR_Y_MAX,
793                                                  size, size,
794                                                  0, 64 * 360);
795                 }
796         }
797 }
798
799 void drawStick_expose_event(GtkWidget *drawStick, GdkEventExpose *event,
800                             struct stick *stick)
801 {
802         gint width, height;
803
804         gdk_window_get_geometry(drawStick->window, NULL, NULL, &width, &height,
805                                 NULL);
806         gdk_draw_arc(drawStick->window,
807                      drawStick->style->fg_gc[GTK_WIDGET_STATE(drawStick)],
808                                  FALSE,
809                                  0, 0, width-1, height-1, 0, 64*360);
810         if (stick->valid) {
811                 gdk_draw_arc(drawStick->window,
812                              drawStick->style->fg_gc[GTK_WIDGET_STATE(drawStick)],
813                              TRUE,
814                              (double)stick->x/stick->max*width - 2,
815                              (1 - (double)stick->y/stick->max)*height - 2,
816                              3, 3, 0, 64*360);
817         }
818 }
819
820 void btnRead_clicked(void)
821 {
822         static unsigned char buf[CWIID_MAX_READ_LEN];
823         static char txt[CWIID_MAX_READ_LEN*4+50]; /* 3 chars per byte, with
824                                                      * plenty extra */
825         GtkTextIter text_iter;
826         GtkTextMark *p_text_mark;
827         char *cursor;
828         unsigned int offset, len;
829         int flags;
830         unsigned int i;
831
832         /* Decode arguments */
833         offset = strtol(gtk_entry_get_text(GTK_ENTRY(txtReadOffset)), &cursor, 16);
834         if (*cursor != '\0') {
835                 message(GTK_MESSAGE_ERROR, "Invalid read offset", GTK_WINDOW(winRW));
836         }
837
838         len = strtol(gtk_entry_get_text(GTK_ENTRY(txtReadLen)), &cursor, 16);
839         if (*cursor != '\0') {
840                 message(GTK_MESSAGE_ERROR, "Invalid read len", GTK_WINDOW(winRW));
841         }
842         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radReadReg))) {
843                 flags = CWIID_RW_REG;
844         }
845         else {
846                 flags = CWIID_RW_EEPROM;
847         }
848
849         /* Make the call */
850         if (cwiid_read(wiimote, flags, offset, len, buf)) {
851                 message(GTK_MESSAGE_ERROR, "Wiimote read error", GTK_WINDOW(winRW));
852         }
853         else {
854                 /* construct the hexedit-style string */
855                 cursor=txt;
856                 sprintf(cursor, "0x%08X:", offset & ~0xF);
857                 cursor+=11;
858                 for (i=0; i < (offset & 0xF); i++) {
859                         sprintf(cursor, "   ");
860                         cursor+=3;
861                 }
862                 for (i=0; i < len; i++) {
863                         if ((((i + offset) & 0xF) == 0) && (i!=0)) {
864                                 sprintf(cursor, "\n0x%08X:", offset+i);
865                                 cursor+=12;
866                         }
867                         if (((i +offset) & 0x7) == 0) {
868                                 sprintf(cursor, " ");
869                                 cursor++;
870                         }
871                         sprintf(cursor, "%02X ", buf[i]);
872                         cursor+=3;
873                 }
874                 sprintf(cursor, "\n\n");
875
876                 gtk_text_buffer_get_end_iter(tbRW, &text_iter);
877                 p_text_mark = gtk_text_buffer_create_mark(tbRW, NULL, &text_iter,
878                                                           TRUE);
879                 gtk_text_buffer_insert(tbRW, &text_iter, txt, -1);
880                 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(tvRW), p_text_mark, 0.01,
881                                              TRUE, 0.0, 0.0);
882         }
883 }
884
885 char chartox(char c)
886 {
887         char str[2];
888         char *endptr;
889         int val;
890
891         str[0] = c;
892         str[1] = '\0';
893         val = strtol(str, &endptr, 16);
894         if (*endptr != '\0') {
895                 return -1;
896         }
897
898         return (char)val;
899 }
900
901 #define MAX_WRITE_LEN   0x20
902 void btnWrite_clicked(void)
903 {
904         static unsigned char buf[MAX_WRITE_LEN];
905         char *cursor, *data;
906         uint32_t offset;
907         uint16_t len;
908         uint8_t flags;
909
910         /* Decode arguments */
911         offset = strtol(gtk_entry_get_text(GTK_ENTRY(txtWriteOffset)), &cursor, 16);
912         if (*cursor != '\0') {
913                 message(GTK_MESSAGE_ERROR, "Invalid read offset", GTK_WINDOW(winRW));
914         }
915
916         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radWriteReg))) {
917                 flags = CWIID_RW_REG;
918         }
919         else {
920                 flags = CWIID_RW_EEPROM;
921         }
922
923         data = (char *) gtk_entry_get_text(GTK_ENTRY(txtWriteData));
924         cursor = data;
925         len = 0;
926         while (*cursor != '\0') {
927                 if (len > MAX_WRITE_LEN) {
928                         message(GTK_MESSAGE_ERROR, "Message too long", GTK_WINDOW(winRW));
929                         return;
930                 }
931                 /* Trim Leading spaces */
932                 while (*cursor == ' ') {
933                         cursor++;
934                 }
935                 /* Test for end */
936                 if (*cursor == '\0') {
937                         break;
938                 }
939                 /* Read first nibble */
940                 if (!isxdigit((int)*cursor)) {
941                         message(GTK_MESSAGE_ERROR, "Invalid write data",
942                                 GTK_WINDOW(winRW));
943                         return;
944                 }
945                 buf[len] = chartox(*cursor)<<4;
946
947                 /* Read second nibble */
948                 cursor++;
949                 if (!isxdigit((int)*cursor)) {
950                         message(GTK_MESSAGE_ERROR,
951                                 "Invalid write data (digits must come in pairs)",
952                                 GTK_WINDOW(winRW));
953                         return;
954                 }
955                 buf[len] = buf[len] | chartox(*cursor);
956
957                 cursor++;
958                 len++;
959         }
960
961         if (len == 0) {
962                 message(GTK_MESSAGE_ERROR, "No write data", GTK_WINDOW(winRW));
963                 return;
964         }
965
966         /* Make the call */
967         if (cwiid_write(wiimote, flags, offset, len, buf)) {
968                 message(GTK_MESSAGE_ERROR, "Wiimote write error", GTK_WINDOW(winRW));
969         }
970         else {
971                 message(GTK_MESSAGE_INFO, "Wiimote write successful",
972                         GTK_WINDOW(winRW));
973         }
974 }
975
976 void btnRWClose_clicked(void)
977 {
978         gtk_widget_hide(winRW);
979 }
980
981 void btnBeep_clicked(void)
982 {
983         /*if (cwiid_beep(wiimote)) {
984                 message(GTK_MESSAGE_ERROR, "Wiimote sound error", GTK_WINDOW(winMain));
985         }*/
986 }
987
988 void set_report_mode(void)
989 {
990         uint8_t rpt_mode;
991        
992         rpt_mode = CWIID_RPT_STATUS | CWIID_RPT_BTN;
993
994         if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkIR))) {
995                 rpt_mode |= CWIID_RPT_IR;
996         }
997         if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkAcc))) {
998                 rpt_mode |= CWIID_RPT_ACC;
999         }
1000         if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(chkExt))) {
1001                 rpt_mode |= CWIID_RPT_EXT;
1002         }
1003         if (cwiid_set_rpt_mode(wiimote, rpt_mode)) {
1004                 message(GTK_MESSAGE_ERROR, "error setting report mode",
1005                         GTK_WINDOW(winMain));
1006         }
1007 }
1008
1009 #define BATTERY_STR_LEN 14      /* "Battery: 100%" + '\0' */
1010 void cwiid_callback(cwiid_wiimote_t *wiimote, int mesg_count,
1011                     union cwiid_mesg mesg_array[], struct timespec *timestamp)
1012 {
1013         int i;
1014         char battery[BATTERY_STR_LEN];
1015         char *ext_str;
1016         static enum cwiid_ext_type ext_type = CWIID_EXT_NONE;
1017
1018         gdk_threads_enter();
1019         for (i=0; i < mesg_count; i++) {
1020                 switch (mesg_array[i].type) {
1021                 case CWIID_MESG_STATUS:
1022                         snprintf(battery, BATTERY_STR_LEN,"Battery:%d%%",
1023                                  (int) (100.0 * mesg_array[i].status_mesg.battery /
1024                                         CWIID_BATTERY_MAX));
1025                         gtk_statusbar_push(GTK_STATUSBAR(statBattery), 0, battery);
1026                         switch (mesg_array[i].status_mesg.ext_type) {
1027                         case CWIID_EXT_NONE:
1028                                 ext_str = "No extension";
1029                                 break;
1030                         case CWIID_EXT_NUNCHUK:
1031                                 ext_str = "Nunchuk";
1032                                 if (ext_type != CWIID_EXT_NUNCHUK) {
1033                                         if (cwiid_get_acc_cal(wiimote, CWIID_EXT_NUNCHUK,
1034                                                               &nc_cal)) {
1035                                                 message(GTK_MESSAGE_ERROR,
1036                                                         "Unable to retrieve accelerometer calibration",