More callbacks, further GUI work
[metawatch.git] / mw_utility.c
1 /*
2  * (c) 2011 Siegen, Germany by Nils Faerber <nils.faerber@kernelconcepts.de>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdio.h>
23
24 #include "metawatch.h"
25 #include "mw_utility.h"
26
27 #include "fonts.h"
28
29 /* ----------------------------------------------------------------------
30  * Generic drawing functions
31  * ---------------------------------------------------------------------- */
32
33 /*
34  * The pixmap buffer has at least one byte per pixel, even for monochrome (bpp=1)
35  * bitmaps
36  */
37 mw_buffer *mw_alloc_pbuffer(unsigned int res_x, unsigned int res_y, unsigned int bpp)
38 {
39         mw_buffer *nmwbuf;
40         int pbuf_size;
41
42         nmwbuf = (mw_buffer *) malloc(sizeof(mw_buffer));
43         if (!nmwbuf)
44                 return NULL;
45
46         nmwbuf->res_x = res_x;
47         nmwbuf->res_y = res_y;
48         nmwbuf->bpp = bpp;
49
50         pbuf_size = nmwbuf->res_x * nmwbuf->res_y * ((nmwbuf->bpp / 8) + 1);
51         nmwbuf->pbuf = malloc(pbuf_size);
52         if (!nmwbuf->pbuf) {
53                 free(nmwbuf);
54                 return NULL;
55         } else {
56                 memset(nmwbuf->pbuf, 0, pbuf_size);
57                 return nmwbuf;
58         }
59 }
60
61 void mw_free_pbuffer(mw_buffer *mwbuf)
62 {
63         if (!mwbuf)
64                 return;
65
66         free(mwbuf->pbuf);
67         free(mwbuf);
68 }
69
70 /*
71  * makes a buffer for sending to the LCD watch from the drawing buffer, e.g.
72  * mw_send_bitmap(mw_fd, MW_SCREEN_MODE_IDLE, 96, 65, 31, bbuf, len);
73  *
74  * NOT reentrant !
75  */
76 unsigned char *mw_make_mw_buffer(mw_buffer *mwbuf, int *buflen)
77 {
78         static unsigned char wbuf[96*12];
79         int x, y;
80         unsigned char clr;
81
82         memset(wbuf, 0, 96*12);
83
84         for (y = 0; y < mwbuf->res_y; y++) {
85                 for (x = 0; x < mwbuf->res_x; x++) {
86                         clr = *(unsigned char *)(mwbuf->pbuf+((y*mwbuf->res_x)+x));
87                         if (clr) {
88                                 *(unsigned char *)(wbuf+((y*12)+(x/8))) |= 1 << (x%8);
89                         };
90                 };
91         };
92         *buflen = mwbuf->res_y * 12;
93
94         return wbuf;
95 }
96
97 unsigned char *mw_make_mw_oled_buffer(mw_buffer *mwbuf, int *buflen)
98 {
99         static unsigned char wbuf[2*80]; /* size of one OLED, two rows */
100         int x, y;
101         unsigned char clr;
102
103         memset(wbuf, 0, 2*80);
104
105         for (x=0; x<mwbuf->res_x; x++) {
106                 for (y=0; y<mwbuf->res_y; y++) {
107                         clr = *(unsigned char *)(mwbuf->pbuf+((y*mwbuf->res_x)+x));
108                         if (clr) {
109                                 *(unsigned char *)(wbuf+(x+80*(y/8))) |= 1 << (7-(y%8));
110                         }
111                 }
112         }
113         *buflen = (mwbuf->res_y / 8) * 80;
114
115         return wbuf;
116 }
117
118 void mw_dump_mw_buffer(mw_buffer *mwbuf)
119 {
120         int x, y;
121         unsigned char clr;
122
123         for (y = 0; y < mwbuf->res_y; y++) {
124                 for (x = 0; x < mwbuf->res_x; x++) {
125                         clr = *(unsigned char *)(mwbuf->pbuf+((y*mwbuf->res_x)+x));
126                         if (clr)
127                                 fprintf(stderr, ".");
128                         else
129                                 fprintf(stderr, " ");
130                 };
131                 fprintf(stderr, "\n");
132         };
133 }
134
135
136 /* clear/fill entire buffer with color */
137 void mw_buf_clear(mw_buffer *mwbuf, mw_color clr)
138 {
139         int pbuf_size;
140
141         if (!mwbuf)
142                 return;
143         if (clr == MW_TRANSPARENT)
144                 return;
145
146         pbuf_size = mwbuf->res_x * mwbuf->res_y * ((mwbuf->bpp / 8) + 1);
147         memset(mwbuf->pbuf, clr, pbuf_size);
148 }
149
150 /* draw a single pixel */
151 void mw_buf_draw_pixel(mw_buffer *mwbuf, unsigned int x, unsigned int y, mw_color clr)
152 {
153         if (!mwbuf)
154                 return;
155         if (clr == MW_TRANSPARENT)
156                 return;
157
158         *(unsigned char *)(mwbuf->pbuf+((y*mwbuf->res_x)+x)) = clr;
159 }
160
161 void mw_buf_print(mw_buffer *mwbuf, unsigned int x, unsigned int y, char *text, unsigned char fsize, mw_color fgclr, mw_color bgclr)
162 {
163         unsigned int  i,j,z;
164         const unsigned char *data, *font_style;
165         unsigned char mask,xme,yme,offset;
166
167         if (text==NULL || strlen(text) == 0)
168                 return;
169
170         switch (fsize) {
171                 case 0:
172                         data = (const unsigned char *)FONT6x8;
173                         font_style = (const unsigned char *)FONT6x8;
174                         break;
175                 case 1:
176                         data = (const unsigned char *)FONT8x8F;
177                         font_style = (const unsigned char *)FONT8x8F;
178                         break;
179                 case 2:
180                         data = (const unsigned char *)FONT8x16;
181                         font_style = (const unsigned char *)FONT8x16;
182                         break;
183                 default:
184                         data = (const unsigned char *)FONT6x8;
185                         font_style = (const unsigned char *)FONT6x8;
186                         break;
187         };
188         xme = *data++;
189         yme = *data++;
190         offset = *data;
191
192         do {
193                 mask = 0x00;
194                 data =  (font_style + offset) + (offset * (int)(*text - 32));
195                 for (i=0; i < yme; i++) {
196                         mask |=0x80;
197                         for (j=x; j < (x + xme); j++) {
198                                 z = y + i;
199                                 if ((z < mwbuf->res_y) && (j < mwbuf->res_x)) {
200                                         if (*data & mask) {
201                                                 mw_buf_draw_pixel(mwbuf, j, z, fgclr);
202                                         } else {
203                                                 mw_buf_draw_pixel(mwbuf, j, z, bgclr);
204                                         }
205                                 }
206                                 mask >>= 1;
207                         }
208                         data++;
209                 }
210                 x += xme;
211                 text++;
212         } while (*text != '\0');
213 }
214
215 void mw_buf_draw_line_bresenham(mw_buffer *mwbuf, unsigned int xstart, unsigned int ystart, unsigned int xend, unsigned int yend, mw_color clr)
216 {
217         int x, y, t, dx, dy, incx, incy, pdx, pdy, ddx, ddy, es, el, err;
218  
219         dx = xend - xstart;
220         dy = yend - ystart;
221  
222         incx = (dx >= 0) ? 1 : -1;
223         incy = (dy >= 0) ? 1 : -1;
224
225         if (dx<0)
226                 dx = -dx;
227         if (dy<0)
228                 dy = -dy;
229  
230         if (dx>dy) {
231                 pdx = incx; pdy = 0;
232                 ddx=incx; ddy=incy;
233                 es =dy;   el =dx;
234         } else {
235                 pdx=0;    pdy=incy;
236                 ddx=incx; ddy=incy;
237                 es =dx;   el =dy;
238         }
239  
240         x = xstart;
241         y = ystart;
242         err = el/2;
243         mw_buf_draw_pixel(mwbuf, x, y, clr);
244  
245         for (t = 0; t < el; ++t) {
246                 err -= es; 
247                 if (err < 0) {
248                         err += el;
249                         x += ddx;
250                         y += ddy;
251                 } else {
252                         x += pdx;
253                         y += pdy;
254                 }
255                 mw_buf_draw_pixel(mwbuf, x, y, clr);
256         }
257 }
258
259
260 /* ----------------------------------------------------------------------
261  * Complex combined functions, for user convenience
262  * ---------------------------------------------------------------------- */
263
264 /*
265  * send a text notification, automatically take care of device type (ana/digi)
266  * char *title is displayed inverse in top line
267  * char *text is the notification text
268  * vibrate is the number of 300ms vibrations, 0 for none
269  */
270 void mw_do_notification(mwdevice_t *mwdevice, char *title, char *text, unsigned char vibrate)
271 {
272         mw_buffer *mwbuf;
273         unsigned char *bbuf;
274         int len,i,c,r;
275         char sstr[32];
276
277         // fprintf(stderr, "do_notify devtype=%d, title='%s', text='%s', vibrate=%d\n", mwdevice->devtype, title, text, vibrate);
278         if (mwdevice->devtype == MW_DEVICE_TYPE_DIGITAL || mwdevice->devtype == MW_DEVICE_TYPE_DEVB_DIGI) {
279                 mwbuf = mw_alloc_pbuffer(96, 96, 1);
280                 mw_buf_clear(mwbuf, MW_BLACK);
281
282                 mw_buf_print(mwbuf, 0,  0, title, 0, MW_BLACK, MW_WHITE);
283
284                 i=0;
285                 c=0; r=1;
286                 memset(sstr,0,32);
287                 while (i<strlen(text)) {
288                         sstr[c++] = text[i++];
289                         if (c>=16 || i>=strlen(text)) {
290                                 mw_buf_print(mwbuf, 0,  r*9, sstr, 0, MW_WHITE, MW_BLACK);
291                                 memset(sstr,0,32);
292                                 c=0; r++;
293                                 if (r>10)
294                                         break;
295                         };
296                 };
297
298                 bbuf = mw_make_mw_buffer(mwbuf, &len);
299                 mw_send_bitmap(mwdevice, MW_SCREEN_MODE_NOTIFICATION, 96, 96, 0, bbuf, len);
300                 mw_update_display(mwdevice, MW_SCREEN_MODE_NOTIFICATION, 1);
301                 mw_free_pbuffer(mwbuf);
302         } else if (mwdevice->devtype == MW_DEVICE_TYPE_ANA_DIGI || mwdevice->devtype == MW_DEVICE_TYPE_DEVB_ANA_DIGI) {
303                 fprintf(stderr, "do notify OLED\n");
304                 mwbuf = mw_alloc_pbuffer(80, 16, 1);
305                 mw_buf_clear(mwbuf, MW_BLACK);
306
307                 mw_buf_print(mwbuf, 0,  0, title, 0, MW_BLACK, MW_WHITE);
308
309                 i=0;
310                 c=0; r=1;
311                 memset(sstr,0,32);
312                 while (i<strlen(text) && r<2) {
313                         sstr[c++] = text[i++];
314                         if (c>=13 || i>=strlen(text)) {
315                                 mw_buf_print(mwbuf, 0,  r*9, sstr, 0, MW_WHITE, MW_BLACK);
316                                 memset(sstr,0,32);
317                                 c=0; r++;
318                         };
319                 };
320
321                 bbuf = mw_make_mw_oled_buffer(mwbuf, &len);
322                 mw_write_oled_buffer(mwdevice, 0, MW_OLED_UPPER, 80, 0, bbuf, len);
323
324                 mw_buf_clear(mwbuf, MW_BLACK);
325                 c=0; r=0;
326                 memset(sstr,0,32);
327                 while (i<strlen(text) && r<2) {
328                         sstr[c++] = text[i++];
329                         if (c>=13 || i>=strlen(text)) {
330                                 mw_buf_print(mwbuf, 0,  r*9, sstr, 0, MW_WHITE, MW_BLACK);
331                                 memset(sstr,0,32);
332                                 c=0; r++;
333                                 if (r>2)
334                                         break;
335                         };
336                 };
337
338                 bbuf = mw_make_mw_oled_buffer(mwbuf, &len);
339                 mw_write_oled_buffer(mwdevice, 0, MW_OLED_LOWER, 80, 0, bbuf, len);
340
341                 mw_free_pbuffer(mwbuf);
342         } else
343                 fprintf(stderr, "Watch type not set - forgot to call mw_init()?\n");
344
345         if (vibrate)
346                 mw_set_vibrate_mode(mwdevice, 1, 300, 300, vibrate);
347 }
348