b4dbab98e8b8d0b131595867bf61f1f9980a2d9e
[metawatch.git] / gtk-gui / mw-client.c
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <time.h>
9 #include <termios.h>
10 #include <ctype.h>
11
12 #include <errno.h>
13
14 #include <bluetooth/bluetooth.h>
15 #include <bluetooth/rfcomm.h>
16
17 #include <glib.h>
18 #include <dbus/dbus.h>
19 #include <dbus/dbus-glib.h>
20 #include <dbus/dbus-glib-lowlevel.h>
21
22 #include <gtk/gtk.h>
23
24 #include <metawatch.h>
25 #include <crc16ccitt.h>
26 #include <mw_utility.h>
27 #include <bt_helper.h>
28
29 typedef struct {
30         // GMainLoop *mloop;
31         GtkBuilder *builder;
32         mwdevice_t mwdevice;
33         unsigned char rcvbuf[128];
34         int rcvbuf_pos;
35         int con_fd;             /* console input fd */
36         char cmdline[128];
37         int cmdline_pos;
38         int bat_timeout_id;
39 } mwdata_t;
40
41
42 gboolean battery_level_get_timeout(gpointer user_data)
43 {
44         mwdata_t *mdata = (mwdata_t *)user_data;
45
46         mw_send_frame(&mdata->mwdevice, MW_READ_BATTERY_VOLTAGE_MSG, 0, NULL, 0);
47         return TRUE;
48 }
49
50 void mw_get_battery_voltage_response_cb(mwdevice_t *mwdevice, unsigned short *voltage, unsigned char *pgood, unsigned char *charging, void *user_data)
51 {
52         mwdata_t *mdata = (mwdata_t *)user_data;
53         gdouble volt = *voltage;
54         GtkAdjustment *batadjust;
55         GtkProgressBar *batbar;
56         gchar batstr[32];
57
58         batadjust = GTK_ADJUSTMENT(gtk_builder_get_object (mdata->builder, "bat_adjust"));
59         gtk_adjustment_set_value(batadjust, volt);
60         batbar = GTK_PROGRESS_BAR(gtk_builder_get_object (mdata->builder, "battery_status_bar"));
61         snprintf(batstr, 32, "%4.0fmV", volt);
62         gtk_progress_bar_set_text(batbar, batstr);
63 }
64
65 void on_rtc_button_clicked (GtkButton *button, gpointer user_data)
66 {
67         mwdata_t *mdata = (mwdata_t *)user_data;
68
69         mw_send_frame(&mdata->mwdevice, MW_GET_REAL_TIME_CLOCK, 0, NULL, 0);
70 }
71
72 void on_notify_button_clicked (GtkButton *button, gpointer user_data)
73 {
74         mwdata_t *mdata = (mwdata_t *)user_data;
75
76 }
77
78 void bitmap_read(mwdevice_t *mwdevice, char *filename)
79 {
80         int ffd, ret;
81         char rbuf[256];
82         unsigned int width, height, i;
83 #ifdef DEBUG
84         unsigned int x, y;
85 #endif
86         unsigned int rowlength;
87         unsigned char *bmapbuf;
88         // unsigned char mw_buf[24];
89
90         ffd = open(filename, O_RDONLY);
91         if (ffd < 0) {
92                 perror("open");
93                 return;
94         };
95         ret = read(ffd, rbuf, 3);
96         if (rbuf[0] != 'P' || rbuf[1] != '4') {
97                 fprintf(stderr, "not a PBM file\n");
98                 return;
99         }
100         memset(rbuf, 0, 256);
101         i = 0;
102         do {
103                 ret = read(ffd, (rbuf+i), 1);
104         } while (!isspace(rbuf[i++]));
105         width = atoi(rbuf);
106
107         memset(rbuf, 0, 256);
108         i = 0;
109         do {
110                 ret = read(ffd, (rbuf+i), 1);
111         } while (!isspace(rbuf[i++]));
112         height = atoi(rbuf);
113
114         rowlength = ((width / 8) + 1);
115
116         bmapbuf = malloc(rowlength * height);
117
118         ret = read(ffd, bmapbuf, rowlength * height);
119         close(ffd);
120
121 #ifdef DEBUG
122         fprintf(stderr, "row length = %d bytes\n", rowlength);
123         fprintf(stderr, "bitmap resolution is %d x %d\n", width, height);
124         fprintf(stderr, "read %d of %d bytes\n", ret, rowlength * height);
125         fprintf(stderr, "\n");
126         for (y=0; y<height; y++) {
127                 for (x=0; x<rowlength; x++) {
128                         for (i=0; i<8; i++)
129                                 fprintf(stderr, "%c", (bmapbuf[(y*rowlength)+x] & (1<<(7-i))) ? '.' : ' ');
130                 }
131                 fprintf(stderr, "\n");
132         }
133         fprintf(stderr, "\n");
134 #endif
135
136         /* reverse bits and invert the bmap */
137         bmap_buffer_flipinvert(1, 1, bmapbuf, rowlength * height);
138         /* send the buffer to the watch */
139         mw_send_bitmap(mwdevice, MW_SCREEN_MODE_IDLE, width, height, 31, bmapbuf, rowlength * height);
140         /* update the display */
141         mw_update_display(mwdevice, MW_SCREEN_MODE_IDLE, 1);
142
143         free(bmapbuf);
144 }
145
146 void on_bitmap_button_clicked (GtkButton *button, gpointer user_data)
147 {
148         mwdata_t *mdata = (mwdata_t *)user_data;
149         GtkWindow *mwin;
150         GtkWidget *dialog;
151
152         mwin = GTK_WINDOW (gtk_builder_get_object (mdata->builder, "main_win"));
153         dialog = gtk_file_chooser_dialog_new("Bitmap File", mwin, GTK_FILE_CHOOSER_ACTION_OPEN,
154                                                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
155                                                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
156                                                 NULL);
157         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
158                 gchar *filename;
159                 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
160                 /* send bitmap file */
161                 bitmap_read(&mdata->mwdevice, filename);
162         }
163         gtk_widget_destroy(dialog);
164 }
165
166 void mw_get_real_time_clock_response_cb(mwdevice_t *mwdevice, struct tm *mw_tm, void *user_data)
167 {
168         mwdata_t *mdata = (mwdata_t *)user_data;
169         GtkButton *rtc_button;
170         gchar label_str[256];
171
172         //g_print("watch RTC is %s\n", asctime(mw_tm));
173         rtc_button = GTK_BUTTON (gtk_builder_get_object (mdata->builder, "rtc_button"));
174         snprintf(label_str, 256, "RTC\n%s", asctime(mw_tm));
175         label_str[strlen(label_str)-1] = 0;
176         gtk_button_set_label(rtc_button, label_str);
177 }
178
179 gboolean handle_mw_io(GIOChannel *mw_io, GIOCondition condition, gpointer udata)
180 {
181         mwdata_t *mdata = (mwdata_t *)udata;
182         int rcvd;
183         int processed;
184
185         rcvd = read(mdata->mwdevice.mw_fd, mdata->rcvbuf+mdata->rcvbuf_pos, 64);
186 #ifdef DEBUG
187         fprintf(stderr, "read %d bytes:\n", rcvd);
188 #endif
189         if (rcvd > 0) {
190 #ifdef DEBUG
191                 dump_frame(mdata->rcvbuf, rcvd);
192 #endif
193                 processed = decode_frame(&mdata->mwdevice, mdata->rcvbuf, rcvd);
194                 if (processed > 0) {
195                         mdata->rcvbuf_pos -= processed;
196                         if (mdata->rcvbuf_pos > 0)
197                                 g_print("Warning: RCV buffer not yet empty\n");
198                 } else {
199                         /* we should rather seek forward for a next potential frame start */
200                         mdata->rcvbuf_pos = 0;
201                 }
202         }
203
204         return TRUE;
205 }
206
207 void 
208 on_window_destroy (GtkObject *object, gpointer user_data)
209 {
210         gtk_main_quit ();
211 }
212
213
214
215 int
216 main (int argc, char *argv[])
217 {
218         GtkBuilder *builder; 
219         GtkWidget *window;
220         GIOChannel *mw_io;
221         bdaddr_t btaddr;
222         int mw_fd;
223         struct termios tmwfd;
224         mwdata_t mdata;
225
226
227         if (argc != 2) {
228                 fprintf(stderr, "Usage:\n\t%s <devicename>\n", argv[0]);
229                 return 1;
230         };
231
232         crc16ccitt_init();
233
234         if (str2ba(argv[1], &btaddr))
235                 return 1;
236         mw_fd = open_socket(&btaddr, 1);
237         if (mw_fd < 0) {
238                 return 1;
239         } else {
240                 fprintf(stderr, "connected to %s\n", argv[1]);
241         };
242
243         /* we have a connection, RFCOMM socket is on mw_fd */
244         /* make the tty raw */
245         tcgetattr(mw_fd, &tmwfd);
246         cfmakeraw(&tmwfd);
247         tmwfd.c_oflag |= ONLCR | OPOST;
248         tmwfd.c_lflag |= ISIG;
249         tcsetattr(mw_fd, TCSANOW, &tmwfd);
250
251         mdata.mwdevice.mw_fd = mw_fd;
252         mdata.rcvbuf_pos = 0;
253         memset(mdata.rcvbuf, 0, 128);
254
255         gtk_init (&argc, &argv);
256
257         builder = gtk_builder_new ();
258         gtk_builder_add_from_file (builder, "mw-client.glade", NULL);
259         mdata.builder = builder;
260         window = GTK_WIDGET (gtk_builder_get_object (builder, "main_win"));
261         gtk_builder_connect_signals (builder, &mdata);
262
263         //g_object_unref (G_OBJECT (builder));
264         
265         gtk_widget_show (window);                
266
267         mw_io = g_io_channel_unix_new(mw_fd);
268         g_io_add_watch(mw_io, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, handle_mw_io, &mdata);
269
270         mw_init(&mdata.mwdevice, mw_fd);
271
272         mw_set_get_real_time_clock_response_cb(&mdata.mwdevice, mw_get_real_time_clock_response_cb, &mdata);
273         mw_set_get_battery_voltage_response_cb(&mdata.mwdevice, mw_get_battery_voltage_response_cb, &mdata);
274
275         // mw_send_frame(&mdata.mwdevice, MW_READ_BATTERY_VOLTAGE_MSG, 0, NULL, 0);
276
277         mdata.bat_timeout_id = g_timeout_add_seconds(10, battery_level_get_timeout, &mdata);
278
279         gtk_main ();
280
281         return 0;
282 }