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