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_mouse_support_resources.h"
7 #include "guix_medical_mouse_support_specifications.h"
8 #include "demo_guix_medical_mouse_support.h"
9 
10 #define CHART_TIMER                2
11 #define RATE_TIMER                 3
12 #define HR_WIN_ANIMATION_TIMER     4
13 
14 #define CHART_Y_CENTER   60
15 #define CHART_TYPE_SCROLLING 0  /* chart continuously scrolls */
16 #define CHART_TYPE_RETRACE   1  /* chart traces left to right */
17 
18 #define HR_PROMPT_VERTICAL_SHIFT 18
19 #define CHART_SCROLL     4
20 #define CHART_LINE_WIDTH 1
21 #define CHART_AMPLITUDE  1
22 
23 int ekg_values[] = {
24     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 5, 4, 2, 0, 0, 0, 0, -3, 38, -7, 0, 0, 0, 0, 5, 8, 10, 8, 5
25 };
26 
27 #define MIN_HEART_RATE  60
28 #define MAX_HEART_RATE  150
29 
30 GX_RECTANGLE chart_area;
31 int line_y_coords[3];
32 int ekg_index = 0;
33 
34 int current_heart_rate = MIN_HEART_RATE;        // simulation current HR value
35 int heart_rate_step = 1;                        // simulation, HR value delta
36 int chart_type = CHART_TYPE_RETRACE;            // toggle two chart types
37 GX_BOOL warn_heart = GX_FALSE;                  // simulation, high heart rate warning?
38 
39 int retrace_xpos;
40 
41 /*****************************************************************************/
draw_scrolling_chart_lines(GX_CANVAS * canvas,GX_WINDOW * win)42 void draw_scrolling_chart_lines(GX_CANVAS *canvas, GX_WINDOW *win)
43 {
44 GX_RECTANGLE chart_update_rectangle;
45 GX_DRAW_CONTEXT *context;
46 GX_PIXELMAP *map;
47 
48     chart_update_rectangle = chart_area;
49     chart_update_rectangle.gx_rectangle_right = chart_update_rectangle.gx_rectangle_left + (CHART_SCROLL * 2) - 1;
50 
51     /* Initiate drawing on this canvas, clipped to just the leftmost two shift slots.  */
52     gx_canvas_drawing_initiate(canvas, (GX_WIDGET *) win, &chart_update_rectangle);
53     gx_system_draw_context_get(&context);
54 
55     /* paint a slice of the background bitmap to erase the previous line */
56     gx_context_brush_define(GX_COLOR_ID_CHART_LINE, GX_COLOR_ID_WINDOW_FILL, GX_BRUSH_SOLID_FILL|GX_BRUSH_ROUND|GX_BRUSH_ALIAS);
57     gx_context_brush_width_set(0);
58 
59     /* draw a rectangle to erase the old lines: */
60     gx_context_pixelmap_get(GX_PIXELMAP_ID_CHART_ERASE, &map);
61     gx_canvas_pixelmap_draw(chart_area.gx_rectangle_left, chart_area.gx_rectangle_top, map);
62 
63     /* draw the most recent two lines
64        one line is the newly drawn value, and the second
65        line is re-drawn so that it is not clipped
66     */
67     context ->gx_draw_context_brush.gx_brush_width = CHART_LINE_WIDTH;
68     context ->gx_draw_context_brush.gx_brush_style = GX_BRUSH_ROUND|GX_BRUSH_ALIAS;
69 
70     gx_canvas_line_draw(chart_update_rectangle.gx_rectangle_left + 1,
71         chart_update_rectangle.gx_rectangle_top + line_y_coords[0],
72         chart_update_rectangle.gx_rectangle_left + CHART_SCROLL,
73         chart_update_rectangle.gx_rectangle_top + line_y_coords[1]);
74 
75     gx_canvas_line_draw(chart_update_rectangle.gx_rectangle_left + CHART_SCROLL,
76         chart_update_rectangle.gx_rectangle_top + line_y_coords[1],
77         chart_update_rectangle.gx_rectangle_left + (CHART_SCROLL * 2),
78         chart_update_rectangle.gx_rectangle_top + line_y_coords[2]);
79 
80     /* Indicate that drawing on this canvas is complete.  */
81     gx_canvas_drawing_complete(canvas, GX_FALSE);
82 }
83 
84 /*****************************************************************************/
update_scrolling_chart(GX_WINDOW * win)85 VOID update_scrolling_chart(GX_WINDOW *win)
86 {
87 GX_CANVAS *canvas;
88 GX_RECTANGLE block_rect;
89 INT ekg_count;
90 INT ekg_value;
91 INT slider_value;
92 GX_ICON *heart_icon;
93 
94     /* pick up the canvas pointer */
95     gx_widget_canvas_get(win, &canvas);
96 
97     /* Initiate drawing on this canvas.  */
98     gx_canvas_drawing_initiate(canvas, win, &chart_area);
99 
100     /* shift down are previous values */
101     line_y_coords[2] = line_y_coords[1];
102     line_y_coords[1] = line_y_coords[0];
103 
104     ekg_index++;
105     ekg_count = sizeof(ekg_values) / sizeof(int);
106     if (ekg_index >= ekg_count)
107     {
108         ekg_index = 0;
109     }
110 
111     ekg_value = ekg_values[ekg_index];
112     line_y_coords[0] = CHART_Y_CENTER - ekg_value;
113 
114     // the slider scale is 0 to 100, so convert ekg value to
115     // approximate slider value:
116 
117     slider_value = ekg_value + 45;
118     gx_slider_value_set((GX_SLIDER *) &vitals_screen.vitals_screen_cardio_slider,
119         &vitals_screen.vitals_screen_cardio_slider.gx_slider_info, slider_value);
120 
121     /* shift the existing data to the right
122        skip the first segment on the left, because it has
123        been clipped at the chart left edge. So start one
124        segment in, and shift all remaining segments to the right
125     */
126     block_rect = chart_area;
127     block_rect.gx_rectangle_left += CHART_SCROLL;
128 
129     gx_canvas_block_move(&block_rect, CHART_SCROLL, 0, GX_NULL);
130 
131     /* draw the two previous lines
132        the second value will not be unclipped,
133        and the first value is our newest line
134     */
135     draw_scrolling_chart_lines(canvas, win);
136 
137     /* Indicate that drawing on this canvas is complete.  */
138     gx_canvas_drawing_complete(canvas, GX_TRUE);
139 
140     heart_icon = &vitals_screen.vitals_screen_heart_icon;
141 
142     if (ekg_value == 0)
143     {
144         if (warn_heart)
145         {
146             if (heart_icon->gx_icon_normal_pixelmap != GX_PIXELMAP_ID_ICON_HEART_RED_SMALL)
147             {
148                 gx_icon_pixelmap_set(heart_icon, GX_PIXELMAP_ID_ICON_HEART_RED_SMALL, GX_PIXELMAP_ID_ICON_HEART_RED_SMALL);
149             }
150         }
151         else
152         {
153             if (heart_icon->gx_icon_normal_pixelmap != GX_PIXELMAP_ID_ICON_HEART_GRN_SMALL)
154             {
155                 gx_icon_pixelmap_set(heart_icon, GX_PIXELMAP_ID_ICON_HEART_GRN_SMALL, GX_PIXELMAP_ID_ICON_HEART_GRN_SMALL);
156             }
157         }
158     }
159     else
160     {
161         if (ekg_index >= 19 && ekg_index <= 22)
162         {
163             if (warn_heart)
164             {
165                 if (heart_icon->gx_icon_normal_pixelmap != GX_PIXELMAP_ID_ICON_HEART_RED_LARGE)
166                 {
167                     gx_icon_pixelmap_set(heart_icon, GX_PIXELMAP_ID_ICON_HEART_RED_LARGE, GX_PIXELMAP_ID_ICON_HEART_RED_LARGE);
168                 }
169             }
170             else
171             {
172                 if (heart_icon->gx_icon_normal_pixelmap != GX_PIXELMAP_ID_ICON_HEART_GRN_LARGE)
173                 {
174                     gx_icon_pixelmap_set(heart_icon, GX_PIXELMAP_ID_ICON_HEART_GRN_LARGE, GX_PIXELMAP_ID_ICON_HEART_GRN_LARGE);
175                 }
176             }
177         }
178         else
179         {
180             if (warn_heart)
181             {
182                 if (heart_icon->gx_icon_normal_pixelmap != GX_PIXELMAP_ID_ICON_HEART_RED_MED)
183                 {
184                     gx_icon_pixelmap_set(heart_icon, GX_PIXELMAP_ID_ICON_HEART_RED_MED, GX_PIXELMAP_ID_ICON_HEART_RED_MED);
185                 }
186             }
187             else
188             {
189                 if (heart_icon->gx_icon_normal_pixelmap != GX_PIXELMAP_ID_ICON_HEART_GRN_MED)
190                 {
191                     gx_icon_pixelmap_set(heart_icon, GX_PIXELMAP_ID_ICON_HEART_GRN_MED, GX_PIXELMAP_ID_ICON_HEART_GRN_MED);
192                 }
193             }
194         }
195      }
196  }
197 
198 /*****************************************************************************/
update_retrace_chart(GX_WINDOW * win)199 void update_retrace_chart(GX_WINDOW *win)
200 {
201 GX_CANVAS *canvas;
202 GX_PIXELMAP *map;
203 GX_PIXELMAP *ball;
204 GX_RECTANGLE block_rect;
205 
206     ekg_index++;
207 
208     if (ekg_index >= sizeof(ekg_values) / sizeof(int))
209     {
210         ekg_index = 0;
211     }
212 
213     retrace_xpos += CHART_SCROLL;
214 
215     if (retrace_xpos + CHART_SCROLL > chart_area.gx_rectangle_right)
216     {
217         retrace_xpos = 0;
218     }
219 
220     block_rect.gx_rectangle_top = chart_area.gx_rectangle_top;
221     block_rect.gx_rectangle_bottom = chart_area.gx_rectangle_bottom;
222     block_rect.gx_rectangle_left = chart_area.gx_rectangle_left + retrace_xpos;
223     block_rect.gx_rectangle_right = chart_area.gx_rectangle_left + retrace_xpos + (CHART_SCROLL * 8);
224 
225     /* pick up the canvas pointer */
226     gx_widget_canvas_get(win, &canvas);
227 
228     /* Initiate drawing on this canvas.  */
229     gx_canvas_drawing_initiate(canvas, win, &chart_area);
230     gx_context_pixelmap_get(GX_PIXELMAP_ID_CARDIO_DOT, &ball);
231 
232     /* erase the rectangle ahead of the line */
233     gx_context_pixelmap_get(GX_PIXELMAP_ID_CHART_ERASE, &map);
234     gx_context_brush_define(GX_COLOR_ID_NEON, GX_COLOR_ID_NEON, GX_BRUSH_SOLID_FILL|GX_BRUSH_ROUND|GX_BRUSH_ALIAS);
235     gx_context_brush_width_set(0);
236     gx_canvas_pixelmap_tile(&block_rect, map);
237 
238     gx_context_brush_width_set(1);
239 
240     line_y_coords[1] = line_y_coords[0];
241     line_y_coords[0] = CHART_Y_CENTER - ekg_values[ekg_index];
242 
243     gx_canvas_line_draw(chart_area.gx_rectangle_left + retrace_xpos,
244         chart_area.gx_rectangle_top + line_y_coords[1],
245         chart_area.gx_rectangle_left + retrace_xpos + CHART_SCROLL,
246         chart_area.gx_rectangle_top + line_y_coords[0]);
247 
248     gx_canvas_pixelmap_draw(chart_area.gx_rectangle_left + retrace_xpos + CHART_SCROLL,
249         chart_area.gx_rectangle_top + line_y_coords[0] - ball->gx_pixelmap_height / 2, ball);
250 
251     /* Indicate that drawing on this canvas is complete.  */
252     gx_canvas_drawing_complete(canvas, GX_TRUE);
253 }
254 
255 /*****************************************************************************/
ToggleChartType(VOID)256 VOID ToggleChartType(VOID)
257 {
258     if (chart_type == CHART_TYPE_SCROLLING)
259     {
260         chart_type = CHART_TYPE_RETRACE;
261         gx_widget_shift(&vitals_screen.vitals_screen_current_hr_prompt, 0, -HR_PROMPT_VERTICAL_SHIFT, GX_TRUE);
262         chart_area.gx_rectangle_left = vitals_screen.vitals_screen_waveform_window.gx_window_client.gx_rectangle_left;
263         gx_widget_hide(&vitals_screen.vitals_screen_cardio_slider);
264         gx_widget_hide(&vitals_screen.vitals_screen_heart_icon);
265     }
266     else
267     {
268         chart_type = CHART_TYPE_SCROLLING;
269         chart_area.gx_rectangle_left = vitals_screen.vitals_screen_cardio_slider.gx_widget_size.gx_rectangle_right + 1;
270         gx_widget_shift(&vitals_screen.vitals_screen_current_hr_prompt, 0, HR_PROMPT_VERTICAL_SHIFT, GX_TRUE);
271         gx_widget_show(&vitals_screen.vitals_screen_cardio_slider);
272         gx_widget_show(&vitals_screen.vitals_screen_heart_icon);
273     }
274     gx_system_dirty_mark(&vitals_screen.vitals_screen_waveform_window);
275 }
276 
277 /*****************************************************************************/
update_heart_rate()278 VOID update_heart_rate()
279 {
280 static GX_CHAR heart_rate_string[5];
281 
282     current_heart_rate += heart_rate_step;
283 
284     gx_utility_ltoa(current_heart_rate, heart_rate_string, 5);
285     gx_prompt_text_set(&vitals_screen.vitals_screen_current_hr_prompt, heart_rate_string);
286 
287     if (current_heart_rate >= 130)
288     {
289         warn_heart = GX_TRUE;
290         gx_prompt_text_color_set(&vitals_screen.vitals_screen_current_hr_prompt, GX_COLOR_ID_WARN_YELLOW, GX_COLOR_ID_WARN_YELLOW, GX_COLOR_ID_WARN_YELLOW);
291     }
292     else
293     {
294         warn_heart = GX_FALSE;
295         gx_prompt_text_color_set(&vitals_screen.vitals_screen_current_hr_prompt, GX_COLOR_ID_NEON, GX_COLOR_ID_NEON, GX_COLOR_ID_NEON);
296     }
297 
298     if (heart_rate_step > 0)
299     {
300         if (current_heart_rate >= MAX_HEART_RATE)
301         {
302             heart_rate_step = -1;
303         }
304     }
305     else
306     {
307         if (current_heart_rate <= MIN_HEART_RATE)
308         {
309             heart_rate_step = 1;
310         }
311     }
312 }
313 
314 /*****************************************************************************/
AnimateHRWin(VOID)315 VOID AnimateHRWin(VOID)
316 {
317     INT stop;
318     INT left;
319     INT distance;
320 
321     left = vitals_screen.vitals_screen_hr_win.gx_widget_size.gx_rectangle_left;
322     stop = vitals_screen.vitals_screen_waveform_window.gx_widget_size.gx_rectangle_right + 5;
323 
324     distance = (left - stop) / 3;
325     gx_widget_shift(&vitals_screen.vitals_screen_hr_win, -distance, 0, GX_TRUE);
326 
327     if (distance <= 1)
328     {
329         gx_system_timer_stop(&vitals_screen, HR_WIN_ANIMATION_TIMER);
330     }
331 }
332 
333 
334 /*****************************************************************************/
vitals_screen_event_process(GX_WINDOW * window,GX_EVENT * myevent)335 UINT vitals_screen_event_process(GX_WINDOW *window, GX_EVENT *myevent)
336 {
337 UINT status = GX_SUCCESS;
338 GX_WIDGET *widget = (GX_WIDGET *) window;
339 
340     switch(myevent->gx_event_type)
341     {
342     case GX_EVENT_SHOW:
343         heart_rate_step = 1;
344         line_y_coords[0] = line_y_coords[1] = line_y_coords[2] = 50;
345         ekg_index = 0;
346         chart_area = vitals_screen.vitals_screen_waveform_window.gx_window_client;
347         med_screen_base_event_handler(window, myevent);
348         gx_prompt_text_set(&vitals_screen.vitals_screen_patient_name, GetPatientName());
349         gx_prompt_text_set(&vitals_screen.vitals_screen_admit_date, GetPatientAdmitDate());
350 
351         // Shift the HR window off the right edge of screen
352         gx_widget_shift(&vitals_screen.vitals_screen_hr_win,
353             vitals_screen.base.gx_widget_size.gx_rectangle_right - vitals_screen.vitals_screen_hr_win.gx_widget_size.gx_rectangle_left, 0, GX_TRUE);
354 
355         retrace_xpos = chart_area.gx_rectangle_left;
356 
357         if (chart_type == CHART_TYPE_RETRACE)
358         {
359             gx_widget_hide(&vitals_screen.vitals_screen_heart_icon);
360             gx_widget_hide(&vitals_screen.vitals_screen_cardio_slider);
361         }
362         else
363         {
364             chart_area.gx_rectangle_left = vitals_screen.vitals_screen_cardio_slider.gx_widget_size.gx_rectangle_right + 1;
365         }
366         gx_system_timer_start(widget, CHART_TIMER, 2, 2);
367         gx_system_timer_start(widget, RATE_TIMER, GX_TICKS_SECOND * 2, GX_TICKS_SECOND * 2);
368         gx_system_timer_start(widget, HR_WIN_ANIMATION_TIMER, 1, 1);
369         break;
370 
371     case GX_EVENT_HIDE:
372         gx_system_timer_stop(widget, 0);
373         gx_window_event_process(window, myevent);
374         break;
375 
376     case GX_EVENT_PEN_DOWN:
377         if (gx_utility_rectangle_point_detect(&vitals_screen.vitals_screen_hr_win.gx_widget_size, myevent->gx_event_payload.gx_event_pointdata) ||
378             gx_utility_rectangle_point_detect(&vitals_screen.vitals_screen_waveform_window.gx_widget_size, myevent->gx_event_payload.gx_event_pointdata))
379         {
380             ToggleChartType();
381         }
382         break;
383 
384     case GX_EVENT_TIMER:
385         switch(myevent ->gx_event_payload.gx_event_timer_id)
386         {
387         case CHART_TIMER:
388             if (chart_type == CHART_TYPE_SCROLLING)
389             {
390                 update_scrolling_chart(window);
391             }
392             else
393             {
394                 update_retrace_chart(window);
395             }
396             break;
397 
398         case RATE_TIMER:
399             update_heart_rate();
400             break;
401 
402         case HR_WIN_ANIMATION_TIMER:
403             AnimateHRWin();
404             break;
405         }
406         break;
407 
408     default:
409         status = med_screen_base_event_handler(window, myevent);
410     }
411     return status;
412 }
413 
414 
415 /*****************************************************************************/
waveform_draw(GX_WINDOW * window)416 void waveform_draw(GX_WINDOW *window)
417 {
418     gx_window_draw(window);
419 }
420 
421 
422