1 /* This is a small demo of the high-performance GUIX graphics framework. */
2 
3 #include <stdio.h>
4 #include "gx_api.h"
5 
6 #include "guix_medical_resources.h"
7 #include "guix_medical_specifications.h"
8 #include "demo_guix_medical.h"
9 
10 /* Define timer ids.  */
11 #define EKG_WAVEFORM_TIMER    2
12 #define PULSE_WAVEFORM_TIMER  3
13 #define HEART_RATE_TIMER      4
14 #define SPO2_TIMER            5
15 #define VALUE_ANIMATION_TIMER 6
16 
17 /* Define constants.  */
18 #define CHART_SCROLL                 4
19 #define VALUE_ANIOMATION_TOTAL_STEPS 10
20 
21 #define CARDIO_DOT_WIDTH  9
22 #define CARDIO_DOT_HEIGHT 10
23 
24 /* Define a macro to calculate the current value according to the animation step.  */
25 #define GET_NEW_VALUE(info) (info.start_value + (info.end_value - info.current_value) * value_animation_step / VALUE_ANIOMATION_TOTAL_STEPS)
26 
27 /* Define a waveform information structure for waveform drawing.  */
28 typedef struct WAVE_INFO_STRUCT
29 {
30     GX_COLOR *capture_memory;
31     INT capture_memory_size;
32     INT capture_xpos;
33     INT capture_ypos;
34     INT *value_array;
35     INT  total_values;
36     INT  index;
37     INT  retrace_xpos;
38     GX_RESOURCE_ID color_id;
39 }WAVE_INFO;
40 
41 /* Define a value information structure for value animation.  */
42 typedef struct VALUE_INFO_STRUCT
43 {
44     INT start_value;
45     INT end_value;
46     INT current_value;
47 }VALUE_INFO;
48 
49 /* Define ekg waveform values.  */
50 INT ekg_values[] = {
51     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 5, 4, 2, 0, 0, 0, 0, -3, 30, -7, 0, 0, 0, 0, 5, 8, 10, 8, 5
52 };
53 
54 /* Define pulse waveform values.  */
55 INT pulse_values[] = {
56     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 5, 4, 2, 0, 0, 0, 0, -3, 30, -7, 0, 0, 0, 0, 5, 8, 10, 8, 5
57 };
58 
59 /* Define heart rate values.  */
60 INT heart_rate_values[] = {
61     88, 87, 86, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 94, 93, 92, 90, 89
62 };
63 
64 /* Define spo2 values.  */
65 INT spo2_values[] = {
66     94, 95, 96, 97, 96, 95, 94, 93
67 };
68 
69 /* Define waveform capture memory.  */
70 GX_COLOR ekg_capture_memory[CARDIO_DOT_WIDTH * CARDIO_DOT_HEIGHT];
71 GX_COLOR pulse_capture_memory[CARDIO_DOT_WIDTH * CARDIO_DOT_HEIGHT];
72 
73 /* Define and initialize ekg waveform information instance.  */
74 WAVE_INFO ekg_wave_info = {
75     ekg_capture_memory,
76     CARDIO_DOT_WIDTH* CARDIO_DOT_HEIGHT * sizeof(GX_COLOR),
77     0,
78     0,
79     ekg_values,
80     (INT)(sizeof(ekg_values) / sizeof(INT)),
81     0,
82     0,
83     GX_COLOR_ID_GREEN
84 };
85 
86 /* Define and initialize pulse waveform information instance.  */
87 WAVE_INFO pulse_wave_info = {
88     pulse_capture_memory,
89     CARDIO_DOT_WIDTH* CARDIO_DOT_HEIGHT * sizeof(GX_COLOR),
90     0,
91     0,
92     pulse_values,
93     (INT)(sizeof(pulse_values) / sizeof(INT)),
94     0,
95     0,
96     GX_COLOR_ID_BLUE
97 };
98 
99 INT heart_rate_index = 0;
100 INT spo2_index = 0;
101 INT value_animation_step = 0;
102 VALUE_INFO insulin = { 48, 71, 48 };
103 VALUE_INFO heart_rate = { 0, 88, 0 };
104 VALUE_INFO spo2 = { 0, 94, 0 };
105 VALUE_INFO medtype1 = { 0, 55, 0 };
106 VALUE_INFO medtype2 = { 0, 25, 0 };
107 VALUE_INFO medtype3 = { 0, 45, 0 };
108 
109 /* Define prototypes.  */
110 extern VOID GetPatientName(GX_STRING* string);
111 extern VOID GetPatientDOB(GX_STRING* string);
112 extern VOID GetPatientAge(INT* age);
113 
114 /******************************************************************************************/
115 /* Capture canvas under cardio dot.                                                       */
116 /******************************************************************************************/
capture_memory(GX_CANVAS * canvas,WAVE_INFO * info)117 static VOID capture_memory(GX_CANVAS *canvas, WAVE_INFO *info)
118 {
119 GX_COLOR *get;
120 GX_COLOR *put;
121 INT       copy_width;
122 INT       y;
123 
124     get = canvas->gx_canvas_memory;
125     get += info->capture_ypos * canvas->gx_canvas_x_resolution;
126     get += info->capture_xpos;
127 
128     put = info->capture_memory;
129     copy_width = CARDIO_DOT_WIDTH * sizeof(GX_COLOR);
130 
131     for (y = 0; y < CARDIO_DOT_HEIGHT; y++)
132     {
133         memcpy(put, get, copy_width);
134 
135         put += CARDIO_DOT_WIDTH;
136         get += canvas->gx_canvas_x_resolution;
137     }
138 }
139 
140 /******************************************************************************************/
141 /* Restore canvas under cardio dot.                                                       */
142 /******************************************************************************************/
restore_memory(GX_CANVAS * canvas,WAVE_INFO * info)143 static VOID restore_memory(GX_CANVAS *canvas, WAVE_INFO *info)
144 {
145 GX_PIXELMAP pixelmap;
146 
147     memset(&pixelmap, 0, sizeof(GX_PIXELMAP));
148     pixelmap.gx_pixelmap_data = (GX_UBYTE *)info->capture_memory;
149     pixelmap.gx_pixelmap_data_size = info->capture_memory_size;
150     pixelmap.gx_pixelmap_width = CARDIO_DOT_WIDTH;
151     pixelmap.gx_pixelmap_height = CARDIO_DOT_HEIGHT;
152     pixelmap.gx_pixelmap_format = GX_COLOR_FORMAT_24XRGB;
153 
154     gx_canvas_pixelmap_draw(info->capture_xpos, info->capture_ypos, &pixelmap);
155 }
156 
157 /******************************************************************************************/
158 /* Update waveform.                                                                       */
159 /******************************************************************************************/
update_waveform(GX_WINDOW * win,WAVE_INFO * info)160 static VOID update_waveform(GX_WINDOW *win, WAVE_INFO *info)
161 {
162 GX_CANVAS    *canvas;
163 GX_PIXELMAP  *map;
164 GX_RECTANGLE *chart_area;
165 INT           xpos;
166 INT           ycenter;
167 INT           line_start_offset;
168 INT           line_end_offset;
169 
170     chart_area = &win->gx_window_client;
171 
172     /* pick up the canvas pointer.  */
173     gx_widget_canvas_get(win, &canvas);
174 
175     /* Initiate drawing on this canvas.  */
176     gx_canvas_drawing_initiate(canvas, win, chart_area);
177 
178     /* Erase the rectangle ahead of the line.  */
179     xpos = chart_area->gx_rectangle_left + info->retrace_xpos + 10;
180     gx_context_pixelmap_get(GX_PIXELMAP_ID_CHART_ERASE, &map);
181     gx_context_fill_color_set(GX_COLOR_ID_CANVAS);
182     gx_canvas_pixelmap_draw(xpos, chart_area->gx_rectangle_top, map);
183 
184     if (xpos + map->gx_pixelmap_width > chart_area->gx_rectangle_right)
185     {
186         gx_canvas_pixelmap_draw(chart_area->gx_rectangle_left + xpos - chart_area->gx_rectangle_right,
187                                 chart_area->gx_rectangle_top, map);
188     }
189 
190     xpos = chart_area->gx_rectangle_left +info->retrace_xpos;
191 
192     /* Define brush for line draw.  */
193     gx_context_brush_define(info->color_id, info->color_id, GX_BRUSH_SOLID_FILL | GX_BRUSH_ROUND | GX_BRUSH_ALIAS);
194     gx_context_brush_width_set(2);
195 
196     ycenter = (chart_area->gx_rectangle_top + chart_area->gx_rectangle_bottom) >> 1;
197     line_start_offset = info->value_array[info->index++];
198     if (info->index >= info->total_values)
199     {
200         info->index = 0;
201     }
202     line_end_offset = info->value_array[info->index];;
203 
204     /* Restore canvas under previous cardio dot.  */
205     restore_memory(canvas, info);
206 
207     gx_canvas_line_draw(xpos, ycenter - line_start_offset, xpos + CHART_SCROLL, ycenter - line_end_offset);
208 
209     /* Draw cardio dot.  */
210     gx_context_fill_color_set(info->color_id);
211     info->capture_xpos = xpos + CHART_SCROLL - (CARDIO_DOT_WIDTH / 2);
212     info->capture_ypos = ycenter - line_end_offset - (CARDIO_DOT_HEIGHT / 2);
213 
214     /* Capture canvas under new cardio dot. */
215     capture_memory(canvas, info);
216 
217     /* Draw cardio dot.  */
218     gx_context_pixelmap_get(GX_PIXELMAP_ID_CARDIO_DOT, &map);
219     gx_canvas_pixelmap_draw(info->capture_xpos, info->capture_ypos, map);
220 
221     /* Indicate that drawing on this canvas is complete.  */
222     gx_canvas_drawing_complete(canvas, GX_TRUE);
223 
224     info->retrace_xpos += CHART_SCROLL;
225 
226     if (chart_area->gx_rectangle_left + info->retrace_xpos + CHART_SCROLL > chart_area->gx_rectangle_right)
227     {
228         info->retrace_xpos = 0;
229     }
230 }
231 
232 /******************************************************************************************/
233 /* Update heart rate.                                                                     */
234 /******************************************************************************************/
update_heart_rate()235 static VOID update_heart_rate()
236 {
237     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_hr_value, heart_rate_values[heart_rate_index++]);
238 
239     if (heart_rate_index > (sizeof(heart_rate_values) / sizeof(INT)))
240     {
241         heart_rate_index = 0;
242     }
243 }
244 
245 /******************************************************************************************/
246 /* Update spo2 value.                                                                     */
247 /******************************************************************************************/
update_spo2()248 static VOID update_spo2()
249 {
250     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_spo2_value, spo2_values[spo2_index++]);
251 
252     if (spo2_index > (sizeof(spo2_values) / sizeof(INT)))
253     {
254         spo2_index = 0;
255     }
256 }
257 
258 /******************************************************************************************/
259 /* Start value animation.                                                                 */
260 /******************************************************************************************/
start_value_animation()261 static VOID start_value_animation()
262 {
263     gx_system_timer_start((GX_WIDGET*)&vitals_screen, VALUE_ANIMATION_TIMER, 200 / GX_SYSTEM_TIMER_MS, 40 / GX_SYSTEM_TIMER_MS);
264 
265     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_insulin_value, insulin.start_value);
266     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_hr_value, heart_rate.start_value);
267     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_spo2_value, spo2.start_value);
268     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_medtype1_value, medtype1.start_value);
269     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_medtype2_value, medtype2.start_value);
270     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_medtype3_value, medtype3.start_value);
271     gx_slider_value_set((GX_SLIDER *)&vitals_screen.vitals_screen_medtype1_slider, &vitals_screen.vitals_screen_medtype1_slider.gx_slider_info, medtype1.start_value);
272     gx_slider_value_set((GX_SLIDER*)&vitals_screen.vitals_screen_medtype2_slider, &vitals_screen.vitals_screen_medtype2_slider.gx_slider_info, medtype2.start_value);
273     gx_slider_value_set((GX_SLIDER*)&vitals_screen.vitals_screen_medtype3_slider, &vitals_screen.vitals_screen_medtype3_slider.gx_slider_info, medtype3.start_value);
274 
275     value_animation_step = 0;
276 }
277 
278 /******************************************************************************************/
279 /* Update value animation.                                                                */
280 /******************************************************************************************/
update_value_animation()281 static VOID update_value_animation()
282 {
283 INT new_value;
284 
285     value_animation_step++;
286 
287     new_value = GET_NEW_VALUE(insulin);
288     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_insulin_value, new_value);
289 
290     new_value = GET_NEW_VALUE(heart_rate);
291     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_hr_value, new_value);
292 
293     new_value = GET_NEW_VALUE(spo2);
294     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_spo2_value, new_value);
295 
296     new_value = GET_NEW_VALUE(medtype1);
297     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_medtype1_value, new_value);
298     gx_slider_value_set((GX_SLIDER*)&vitals_screen.vitals_screen_medtype1_slider, &vitals_screen.vitals_screen_medtype1_slider.gx_slider_info, new_value);
299 
300     new_value = GET_NEW_VALUE(medtype2);
301     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_medtype2_value, medtype2.start_value);
302     gx_slider_value_set((GX_SLIDER*)&vitals_screen.vitals_screen_medtype2_slider, &vitals_screen.vitals_screen_medtype2_slider.gx_slider_info, new_value);
303 
304     new_value = GET_NEW_VALUE(medtype3);
305     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_medtype3_value, medtype3.start_value);
306     gx_slider_value_set((GX_SLIDER*)&vitals_screen.vitals_screen_medtype3_slider, &vitals_screen.vitals_screen_medtype3_slider.gx_slider_info, new_value);
307 
308     if (value_animation_step == VALUE_ANIOMATION_TOTAL_STEPS)
309     {
310         gx_system_timer_stop((GX_WIDGET*)&vitals_screen, VALUE_ANIMATION_TIMER);
311     }
312 }
313 
314 /******************************************************************************************/
315 /* Start ekg waveform drawing.                                                            */
316 /******************************************************************************************/
start_ekg_waveform()317 static VOID start_ekg_waveform()
318 {
319     ekg_wave_info.index = 0;
320     ekg_wave_info.retrace_xpos = 0;
321     memset(ekg_wave_info.capture_memory, 0, ekg_wave_info.capture_memory_size);
322 
323     gx_system_timer_start((GX_WIDGET*)&vitals_screen, EKG_WAVEFORM_TIMER, 80 / GX_SYSTEM_TIMER_MS, 80 / GX_SYSTEM_TIMER_MS);
324     gx_system_timer_start((GX_WIDGET*)&vitals_screen, HEART_RATE_TIMER, GX_TICKS_SECOND * 2, GX_TICKS_SECOND * 2);
325 }
326 
327 /******************************************************************************************/
328 /* Start pulse waveform drawing.                                                          */
329 /******************************************************************************************/
start_pulse_waveform()330 static VOID start_pulse_waveform()
331 {
332     pulse_wave_info.index = 0;
333     pulse_wave_info.retrace_xpos = 0;
334     memset(pulse_wave_info.capture_memory, 0, pulse_wave_info.capture_memory_size);
335 
336     gx_system_timer_start((GX_WIDGET *)&vitals_screen, PULSE_WAVEFORM_TIMER, 80 / GX_SYSTEM_TIMER_MS, 80 / GX_SYSTEM_TIMER_MS);
337     gx_system_timer_start((GX_WIDGET*)&vitals_screen, SPO2_TIMER, GX_TICKS_SECOND, GX_TICKS_SECOND);
338 }
339 
340 /******************************************************************************************/
341 /* Update patient information.                                                            */
342 /******************************************************************************************/
update_patient_information()343 static VOID update_patient_information()
344 {
345 GX_STRING string;
346 INT       age;
347 
348     /* Get patient's name.  */
349     GetPatientName(&string);
350 
351     /* Set patient's name.  */
352     gx_prompt_text_set_ext(&vitals_screen.vitals_screen_patient_name, &string);
353 
354     /* Get patient's date of birth.  */
355     GetPatientDOB(&string);
356 
357     /* Set patient's date of birth.  */
358     gx_prompt_text_set_ext(&vitals_screen.vitals_screen_patient_dob, &string);
359 
360     /* Get patient's age.  */
361     GetPatientAge(&age);
362 
363     /* Set patient's age.  */
364     gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_patient_age, age);
365 }
366 
367 /******************************************************************************************/
368 /* Override the default event processing of "vitals_screen" to handle signals from my     */
369 /* child widgets.                                                                         */
370 /******************************************************************************************/
vitals_screen_event_process(GX_WINDOW * window,GX_EVENT * myevent)371 UINT vitals_screen_event_process(GX_WINDOW *window, GX_EVENT *myevent)
372 {
373 UINT status = GX_SUCCESS;
374 
375     switch(myevent->gx_event_type)
376     {
377     case GX_EVENT_SHOW:
378 
379         /* Call the event process of the template on which the vitals screen is based.  */
380         template_event_handler(window, myevent);
381 
382         /* Update patient's information.  */
383         update_patient_information();
384 
385         /* Start value animation.  */
386         start_value_animation();
387         break;
388 
389     case GX_EVENT_HIDE:
390 
391         /* Call the event process of the template on which the vitals screen is based.  */
392         template_event_handler(window, myevent);
393 
394         /* Stop all timers that belongs to the window. */
395         gx_system_timer_stop((GX_WIDGET*)window, 0);
396         break;
397 
398     case GX_SIGNAL(ID_MEDTYPE1_SLIDER, GX_EVENT_SLIDER_VALUE):
399         gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_medtype1_value, myevent->gx_event_payload.gx_event_longdata);
400         break;
401 
402     case GX_SIGNAL(ID_MEDTYPE2_SLIDER, GX_EVENT_SLIDER_VALUE):
403         gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_medtype2_value, myevent->gx_event_payload.gx_event_longdata);
404         break;
405 
406     case GX_SIGNAL(ID_MEDTYPE3_SLIDER, GX_EVENT_SLIDER_VALUE):
407         gx_numeric_prompt_value_set(&vitals_screen.vitals_screen_medtype3_value, myevent->gx_event_payload.gx_event_longdata);
408         break;
409 
410     case GX_EVENT_TIMER:
411         switch(myevent ->gx_event_payload.gx_event_timer_id)
412         {
413         case EKG_WAVEFORM_TIMER:
414             update_waveform(&vitals_screen.vitals_screen_ekg_waveform_win, &ekg_wave_info);
415             break;
416 
417         case PULSE_WAVEFORM_TIMER:
418             update_waveform(&vitals_screen.vitals_screen_pulse_waveform_win, &pulse_wave_info);
419             break;
420 
421         case HEART_RATE_TIMER:
422             update_heart_rate();
423             break;
424 
425         case SPO2_TIMER:
426             update_spo2();
427             break;
428 
429         case VALUE_ANIMATION_TIMER:
430             update_value_animation();
431             break;
432         }
433         break;
434 
435     case GX_EVENT_ANIMATION_COMPLETE:
436         if (myevent->gx_event_sender == ID_EKG_WIN_SLIDE_IN)
437         {
438             start_ekg_waveform();
439         }
440         else if (myevent->gx_event_sender = ID_PULSE_WIN_SLIDE_IN)
441         {
442             start_pulse_waveform();
443         }
444         break;
445 
446     default:
447         status = template_event_handler(window, myevent);
448     }
449     return status;
450 }
451 
452 /******************************************************************************************/
453 /* Callback function to format insulin value.                                             */
454 /******************************************************************************************/
insulin_value_format(GX_NUMERIC_PROMPT * prompt,INT value)455 VOID insulin_value_format(GX_NUMERIC_PROMPT *prompt, INT value)
456 {
457     prompt->gx_numeric_prompt_buffer[0] = '0';
458     prompt->gx_numeric_prompt_buffer[1] = '.';
459     gx_utility_ltoa(value, prompt->gx_numeric_prompt_buffer + 2, GX_NUMERIC_PROMPT_BUFFER_SIZE - 1);
460 }
461 
462 
463