Some minor bug fixes (prevent crashes in dana and upon quit)
[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         if (x < 0 || x > 128 || y < 0 || y > 128)
159                 return;
160
161         *(unsigned char *)(mwbuf->pbuf+((y*mwbuf->res_x)+x)) = clr;
162 }
163
164 void mw_buf_print(mw_buffer *mwbuf, unsigned int x, unsigned int y, char *text, unsigned char fsize, mw_color fgclr, mw_color bgclr)
165 {
166         unsigned int  i,j,z;
167         const unsigned char *data, *font_style;
168         unsigned char mask,xme,yme,offset;
169
170         if (text==NULL || strlen(text) == 0)
171                 return;
172
173         switch (fsize) {
174                 case 0:
175                         data = (const unsigned char *)FONT6x8;
176                         font_style = (const unsigned char *)FONT6x8;
177                         break;
178                 case 1:
179                         data = (const unsigned char *)FONT8x8F;
180                         font_style = (const unsigned char *)FONT8x8F;
181                         break;
182                 case 2:
183                         data = (const unsigned char *)FONT8x16;
184                         font_style = (const unsigned char *)FONT8x16;
185                         break;
186                 default:
187                         data = (const unsigned char *)FONT6x8;
188                         font_style = (const unsigned char *)FONT6x8;
189                         break;
190         };
191         xme = *data++;
192         yme = *data++;
193         offset = *data;
194
195         do {
196                 mask = 0x00;
197                 data =  (font_style + offset) + (offset * (int)(*text - 32));
198                 for (i=0; i < yme; i++) {
199                         mask |=0x80;
200                         for (j=x; j < (x + xme); j++) {
201                                 z = y + i;
202                                 if ((z < mwbuf->res_y) && (j < mwbuf->res_x)) {
203                                         if (*data & mask) {
204                                                 mw_buf_draw_pixel(mwbuf, j, z, fgclr);
205                                         } else {
206                                                 mw_buf_draw_pixel(mwbuf, j, z, bgclr);
207                                         }
208                                 }
209                                 mask >>= 1;
210                         }
211                         data++;
212                 }
213                 x += xme;
214                 text++;
215         } while (*text != '\0');
216 }
217
218 void mw_buf_draw_line_bresenham(mw_buffer *mwbuf, unsigned int xstart, unsigned int ystart, unsigned int xend, unsigned int yend, mw_color clr)
219 {
220         int x, y, t, dx, dy, incx, incy, pdx, pdy, ddx, ddy, es, el, err;
221  
222         dx = xend - xstart;
223         dy = yend - ystart;
224  
225         incx = (dx >= 0) ? 1 : -1;
226         incy = (dy >= 0) ? 1 : -1;
227
228         if (dx<0)
229                 dx = -dx;
230         if (dy<0)
231                 dy = -dy;
232  
233         if (dx>dy) {
234                 pdx = incx; pdy = 0;
235                 ddx=incx; ddy=incy;
236                 es =dy;   el =dx;
237         } else {
238                 pdx=0;    pdy=incy;
239                 ddx=incx; ddy=incy;
240                 es =dx;   el =dy;
241         }
242  
243         x = xstart;
244         y = ystart;
245         err = el/2;
246         mw_buf_draw_pixel(mwbuf, x, y, clr);
247  
248         for (t = 0; t < el; ++t) {
249                 err -= es; 
250                 if (err < 0) {
251                         err += el;
252                         x += ddx;
253                         y += ddy;
254                 } else {
255                         x += pdx;
256                         y += pdy;
257                 }
258                 mw_buf_draw_pixel(mwbuf, x, y, clr);
259         }
260 }
261
262 void mw_buf_draw_line_bresenham_w(mw_buffer *mwbuf, unsigned int xstart, unsigned int ystart, unsigned int xend, unsigned int yend, unsigned char thickness, mw_color clr)
263 {
264         int i, x, y, t, dx, dy, incx, incy, pdx, pdy, ddx, ddy, es, el, err;
265  
266         dx = xend - xstart;
267         dy = yend - ystart;
268  
269         incx = (dx >= 0) ? 1 : -1;
270         incy = (dy >= 0) ? 1 : -1;
271
272         if (dx<0)
273                 dx = -dx;
274         if (dy<0)
275                 dy = -dy;
276  
277         if (dx>dy) {
278                 pdx = incx; pdy = 0;
279                 ddx=incx; ddy=incy;
280                 es =dy;   el =dx;
281         } else {
282                 pdx=0;    pdy=incy;
283                 ddx=incx; ddy=incy;
284                 es =dx;   el =dy;
285         }
286  
287         x = xstart;
288         y = ystart;
289         err = el/2;
290         mw_buf_draw_pixel(mwbuf, x, y, clr);
291         for (i=1; i<thickness; i++) {
292                 mw_buf_draw_pixel(mwbuf, x-i, y, clr);
293                 mw_buf_draw_pixel(mwbuf, x+i, y, clr);
294                 mw_buf_draw_pixel(mwbuf, x, y-i, clr);
295                 mw_buf_draw_pixel(mwbuf, x, y+i, clr);
296         }
297  
298         for (t = 0; t < el; ++t) {
299                 err -= es; 
300                 if (err < 0) {
301                         err += el;
302                         x += ddx;
303                         y += ddy;
304                 } else {
305                         x += pdx;
306                         y += pdy;
307                 }
308                 mw_buf_draw_pixel(mwbuf, x, y, clr);
309                 for (i=1; i<thickness; i++) {
310                         mw_buf_draw_pixel(mwbuf, x-i, y, clr);
311                         mw_buf_draw_pixel(mwbuf, x+i, y, clr);
312                         mw_buf_draw_pixel(mwbuf, x, y-i, clr);
313                         mw_buf_draw_pixel(mwbuf, x, y+i, clr);
314                 }
315         }
316 }
317
318 /* ----------------------------------------------------------------------
319  * Complex combined functions, for user convenience
320  * ---------------------------------------------------------------------- */
321
322 /*
323  * send a text notification, automatically take care of device type (ana/digi)
324  * char *title is displayed inverse in top line
325  * char *text is the notification text
326  * vibrate is the number of 300ms vibrations, 0 for none
327  */
328 void mw_do_notification(mwdevice_t *mwdevice, char *title, char *text, unsigned char vibrate)
329 {
330         mw_buffer *mwbuf;
331         unsigned char *bbuf;
332         int len,i,c,r;
333         char sstr[32];
334
335         // fprintf(stderr, "do_notify devtype=%d, title='%s', text='%s', vibrate=%d\n", mwdevice->devtype, title, text, vibrate);
336         if (mwdevice->devtype == MW_DEVICE_TYPE_DIGITAL || mwdevice->devtype == MW_DEVICE_TYPE_DEVB_DIGI) {
337                 mwbuf = mw_alloc_pbuffer(96, 96, 1);
338                 mw_buf_clear(mwbuf, MW_BLACK);
339
340                 mw_buf_print(mwbuf, 0,  0, title, 0, MW_BLACK, MW_WHITE);
341
342                 i=0;
343                 c=0; r=1;
344                 memset(sstr,0,32);
345                 while (i<strlen(text)) {
346                         sstr[c++] = text[i++];
347                         if (c>=16 || i>=strlen(text)) {
348                                 mw_buf_print(mwbuf, 0,  r*9, sstr, 0, MW_WHITE, MW_BLACK);
349                                 memset(sstr,0,32);
350                                 c=0; r++;
351                                 if (r>10)
352                                         break;
353                         };
354                 };
355
356                 bbuf = mw_make_mw_buffer(mwbuf, &len);
357                 mw_send_bitmap(mwdevice, MW_SCREEN_MODE_NOTIFICATION, 96, 96, 0, bbuf, len);
358                 mw_update_display(mwdevice, MW_SCREEN_MODE_NOTIFICATION, 1);
359                 mw_free_pbuffer(mwbuf);
360         } else if (mwdevice->devtype == MW_DEVICE_TYPE_ANA_DIGI || mwdevice->devtype == MW_DEVICE_TYPE_DEVB_ANA_DIGI) {
361                 fprintf(stderr, "do notify OLED\n");
362                 mwbuf = mw_alloc_pbuffer(80, 16, 1);
363                 mw_buf_clear(mwbuf, MW_BLACK);
364
365                 mw_buf_print(mwbuf, 0,  0, title, 0, MW_BLACK, MW_WHITE);
366
367                 i=0;
368                 c=0; r=1;
369                 memset(sstr,0,32);
370                 while (i<strlen(text) && r<2) {
371                         sstr[c++] = text[i++];
372                         if (c>=13 || i>=strlen(text)) {
373                                 mw_buf_print(mwbuf, 0,  r*9, sstr, 0, MW_WHITE, MW_BLACK);
374                                 memset(sstr,0,32);
375                                 c=0; r++;
376                         };
377                 };
378
379                 bbuf = mw_make_mw_oled_buffer(mwbuf, &len);
380                 mw_write_oled_buffer(mwdevice, 0, MW_OLED_UPPER, 80, 0, bbuf, len);
381
382                 mw_buf_clear(mwbuf, MW_BLACK);
383                 c=0; r=0;
384                 memset(sstr,0,32);
385                 while (i<strlen(text) && r<2) {
386                         sstr[c++] = text[i++];
387                         if (c>=13 || i>=strlen(text)) {
388                                 mw_buf_print(mwbuf, 0,  r*9, sstr, 0, MW_WHITE, MW_BLACK);
389                                 memset(sstr,0,32);
390                                 c=0; r++;
391                                 if (r>2)
392                                         break;
393                         };
394                 };
395
396                 bbuf = mw_make_mw_oled_buffer(mwbuf, &len);
397                 mw_write_oled_buffer(mwdevice, 0, MW_OLED_LOWER, 80, 0, bbuf, len);
398
399                 mw_free_pbuffer(mwbuf);
400         } else
401                 fprintf(stderr, "Watch type not set - forgot to call mw_init()?\n");
402
403         if (vibrate)
404                 mw_set_vibrate_mode(mwdevice, 1, 300, 300, vibrate);
405 }
406