1 /*
2 * Copyright (C) 2000-2005, R3vis Corporation.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program 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
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA,
17 * or visit http://www.gnu.org/copyleft/gpl.html.
18 *
19 * Contributor(s):
20 * Wes Bethel, R3vis Corporation, Marin County, California
21 *
22 * The OpenRM project is located at http://openrm.sourceforge.net/.
23 */
24 /*
25 * $Id: clipper.c,v 1.12 2005/06/12 21:31:00 wes Exp $
26 * $Revision: 1.12 $
27 * $Name: OpenRM-1-6-0-2-c $
28 * $Log: clipper.c,v $
29 * Revision 1.12 2005/06/12 21:31:00 wes
30 * Minor code reorganization.
31 *
32 * Revision 1.11 2005/06/08 18:22:14 wes
33 * Code cleanup to eliminate compiler warnings.
34 *
35 * Revision 1.10 2003/04/13 18:13:23 wes
36 * Updated copyright dates.
37 *
38 * Revision 1.9 2003/01/27 05:07:07 wes
39 * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL.
40 *
41 * Revision 1.8 2003/01/16 22:22:45 wes
42 * Updated all source files to reflect new organization of header files: all
43 * headers that were formerly located in include/rmaux, include/rmv and
44 * include/rmi are now located in include/rm.
45 *
46 * Revision 1.7 2002/06/17 00:34:39 wes
47 * replaced rmSubtreeFrame with rmFrame for v1.4.2.
48 *
49 * Revision 1.6 2001/07/15 22:33:18 wes
50 * Added rmPipeDelete to the end of all demo progs. For those that use
51 * an initfunc, added a new RMnode * parm (which is unused, except for rm2screen).
52 *
53 * Revision 1.5 2001/06/03 19:44:05 wes
54 * Add calls to new rmaux routines to handle window resize events, and
55 * for keyboard event handling.
56 *
57 * Revision 1.4 2001/05/26 14:24:49 wes
58 * Added wireframe cubes.
59 *
60 * Revision 1.3 2001/03/31 16:55:18 wes
61 * Added procmode.h, which defines an RMpipe processing mode used in
62 * most demonstration programs. The default processing mode is
63 * RM_PIPE_MULTISTAGE_VIEW_PARALLEL.
64 *
65 * Revision 1.2 2000/12/02 17:24:32 wes
66 * Version 1.4.0-alpha-1 checkin. See the RELEASENOTES file for
67 * a summary of changes. With this checkin, these demo programs
68 * are no longer compatible with versions of the OpenRM API that
69 * are pre-1.4.0.
70 *
71 * Revision 1.1 2000/08/31 02:14:17 wes
72 * Initial entry.
73 *
74 */
75
76 /*
77 * This program demonstates the use of a transformable clip plane, along
78 * with transformation nesting and polygon draw mode manipulation.
79 * A sphere is positioned at the center of the view, with a red directional
80 * light source pointing in from the left, and a blue directional
81 * light source pointing in from the right. In addition, you'll see
82 * a single quad, which represents the position and orientation of
83 * the clip plane.
84 *
85 * The clip plane may be transformed (rotated) by using Shift+Button3.
86 * Button2 will rotate the entire model, Shift+Button2 will perform
87 * isometric scaling, Button1 translates the entire model parallel to
88 * image plane, and Button3 dollys the camera along the Z axis in
89 * eye coordinates.
90 */
91
92 #include <rm/rm.h>
93 #include <rm/rmaux.h>
94 #include <time.h>
95 #include "procmode.h"
96
97 /*
98 * usage: clipper [-w img_width] [-h img_height]
99 */
100
101 static char MyRootName[]={"MyRoot"};
102 static RMnode *MyRoot;
103 static int img_width=400,img_height=300;
104
105 static RMvertex3D defaultClipPlanePoint={0.0F, 0.0F, 0.0F};
106 static RMvertex3D defaultClipPlaneNormal={0.0F, 0.0F, 1.0F};
107
108 static RMclipPlane clipPlane;
109 static RMnode *quadNode;
110 static RMnode *clipNode;
111 static float saveX, saveY;
112 static RMmatrix startMatrix, quadNodeMatrixInverse;
113 static RMmatrix MyRootMatrix, MyRootMatrixInverse;
114
115 void
usage(char * av[])116 usage(char *av[])
117 {
118 char buf[256];
119 sprintf(buf," usage: %s [-w img_width] [-h img_height] \n",av[0]);
120 rmError(buf);
121 }
122
123 void
parse_args(int ac,char * av[])124 parse_args(int ac,
125 char *av[])
126 {
127 int i;
128
129 i = 1;
130 while (i < ac)
131 {
132 if (strcmp(av[i],"-w") == 0)
133 {
134 i++;
135 sscanf(av[i],"%d",&img_width);
136 }
137 else if (strcmp(av[i],"-h") == 0)
138 {
139 i++;
140 sscanf(av[i],"%d",&img_height);
141 }
142 else
143 {
144 usage(av);
145 exit(-1);
146 }
147 i++;
148 }
149 }
150
151 void
myLighting(RMnode * addToNode)152 myLighting(RMnode *addToNode)
153 {
154 /* set up two lights, one red and one blue */
155 RMlight *l0, *l1;
156 RMcolor4D red = {1.0, 0.1, 0.1, 1.0};
157 RMcolor4D blue= {0.1, 0.1, 1.0, 1.0};
158
159 RMcolor4D specular = {0.5, 0.5, 0.5, 1.0};
160
161 RMvertex3D redDirection = {-3.0, 2.0, 1.0};
162 RMvertex3D blueDirection = {3.0, 2.0, 1.0};
163
164 l0 = rmLightNew();
165 if (l0 == NULL)
166 return;
167
168 rmLightSetType(l0, RM_LIGHT_DIRECTIONAL);
169 rmLightSetColor(l0, NULL, &red, &specular);
170 rmLightSetXYZ (l0, &redDirection);
171
172 l1 = rmLightNew();
173 if (l1 == NULL)
174 return;
175
176 rmLightSetType(l1, RM_LIGHT_DIRECTIONAL);
177 rmLightSetColor(l1, NULL, &blue, &specular);
178 rmLightSetXYZ(l1, &blueDirection);
179
180 rmNodeSetSceneLight(addToNode, RM_LIGHT0, l0);
181 rmNodeSetSceneLight(addToNode, RM_LIGHT1, l1);
182
183 /*
184 * when the lights are added as scene parameters, RM makes a copy
185 * of them, and we don't need our RMlight objects anymore, so we
186 * need to delete them.
187 */
188
189 rmLightDelete(l0);
190 rmLightDelete(l1);
191
192 /* set up the light model/lighting environment */
193 {
194 RMcolor4D defAmbient = {0.2, 0.2, 0.2, 1.0};
195 RMlightModel *lm = rmLightModelNew();
196
197 if (lm == NULL)
198 return;
199
200 rmLightModelSetAmbient(lm, &defAmbient);
201 rmLightModelSetTwoSided (lm, RM_FALSE);
202 rmLightModelSetLocalViewer(lm, RM_FALSE);
203 rmNodeSetSceneLightModel(addToNode, lm);
204
205 rmLightModelDelete (lm);
206 }
207 }
208
209 void
my_set_scene(RMnode * camNode,RMenum stereo_format)210 my_set_scene(RMnode *camNode,
211 RMenum stereo_format)
212 {
213 /*
214 * here, we compute the camera parameters such that all of
215 * the geometry is visible.
216 */
217
218 RMcamera3D *c=rmCamera3DNew();
219
220 /* assign a default view */
221 rmDefaultCamera3D(c);
222
223 /* adjust the default view so that all geom is visible */
224 rmCamera3DComputeViewFromGeometry(c,MyRoot,img_width,img_height);
225
226 if (stereo_format != RM_MONO_CHANNEL)
227 {
228 rmCamera3DSetStereo(c,RM_TRUE);
229 rmCamera3DSetEyeSeparation(c,2.5F);
230 rmCamera3DSetFocalDistance (c,0.707F);
231
232 }
233 rmNodeSetSceneCamera3D(camNode, c);
234 rmCamera3DDelete(c);
235
236 /* use a custom lighting model */
237 myLighting(rmRootNode());
238 }
239
240 void
my_sphere(RMnode * addto,float cx,float cy,float cz,float radius,RMcolor3D * color)241 my_sphere(RMnode *addto,
242 float cx,
243 float cy,
244 float cz,
245 float radius,
246 RMcolor3D *color)
247 {
248 RMprimitive *sprim;
249 RMvertex3D v;
250
251 v.x = cx;
252 v.y = cy;
253 v.z = cz;
254
255 sprim = rmPrimitiveNew(RM_SPHERES);
256 rmPrimitiveSetVertex3D(sprim,1,&v,RM_COPY_DATA,NULL);
257
258 rmPrimitiveSetRadii(sprim, 1, &radius, RM_COPY_DATA, NULL);
259
260 if (color != NULL)
261 rmPrimitiveSetColor3D(sprim, 1, color, RM_COPY_DATA, NULL);
262
263 rmPrimitiveSetModelFlag(sprim,RM_SPHERES_512);
264
265 rmNodeAddPrimitive(addto, sprim);
266 }
267
268 void
my_quad(RMnode * addToNode,RMvertex3D * point,RMvertex3D * normal,float radius)269 my_quad(RMnode *addToNode,
270 RMvertex3D *point,
271 RMvertex3D *normal,
272 float radius)
273 {
274 RMprimitive *p;
275 RMvertex3D v[4], n[4];
276
277 /* need to add code that will create a quad that actually
278 honors the plane equation specified by "normal" */
279 v[0].z = v[1].z = v[2].z = v[3].z = point->z;
280
281 v[0].x = point->x - radius;
282 v[0].y = point->y - radius;
283
284 v[1].x = point->x + radius;
285 v[1].y = point->y - radius;
286
287 v[2].x = point->x + radius;
288 v[2].y = point->y + radius;
289
290 v[3].x = point->x - radius;
291 v[3].y = point->y + radius;
292
293 n[0] = n[1] = n[2] = n[3] = *normal;
294
295 p = rmPrimitiveNew(RM_QUADS);
296
297 rmPrimitiveSetVertex3D(p, 4, v, RM_COPY_DATA, NULL);
298 rmPrimitiveSetNormal3D(p, 4, n, RM_COPY_DATA, NULL);
299
300 rmNodeAddPrimitive(addToNode, p);
301 }
302
303 void
myClipPlane(RMnode * addToNode,RMvertex3D * point,RMvertex3D * normal)304 myClipPlane(RMnode *addToNode,
305 RMvertex3D *point,
306 RMvertex3D *normal)
307 {
308 rmClipPlaneSetPointNormal(&clipPlane, point, normal);
309 rmClipPlaneEnable(&clipPlane);
310
311 rmNodeSetSceneClipPlane(addToNode,RM_SCENE_CLIP_PLANE0, &clipPlane);
312 }
313
314 void
buildInsideStuff(RMnode * addTo,float x,float y,float z,float radius)315 buildInsideStuff(RMnode *addTo,
316 float x,
317 float y,
318 float z,
319 float radius)
320 {
321 RMprimitive *p;
322 RMvertex3D v[4];
323 RMcolor3D c[2];
324
325 p = rmPrimitiveNew(RM_BOX3D);
326
327 v[0].x = x;
328 v[0].y = y;
329 v[0].z = z;
330
331 v[1].x = v[0].x + radius * 0.5;
332 v[1].y = v[0].y + radius * 0.5;
333 v[1].z = v[0].z + radius * 0.5;
334
335 v[2].x = x;
336 v[2].y = y;
337 v[2].z = z;
338
339 v[3].x = v[2].x - radius * 0.5;
340 v[3].y = v[2].y - radius * 0.5;
341 v[3].z = v[2].z - radius * 0.5;
342
343 c[0].r = c[0].g = 1.0;
344 c[0].b = 0.0;
345
346 c[1].r = c[1].b = 0.0;
347 c[1].g = 1.0;
348
349 rmPrimitiveSetVertex3D(p,4, v, RM_COPY_DATA,NULL);
350 rmPrimitiveSetColor3D(p,2,c,RM_COPY_DATA, NULL);
351
352 rmNodeAddPrimitive(addTo, p);
353
354 }
355
356 void
my_build_objs(void)357 my_build_objs(void)
358 {
359 RMnode *sphereNode, *insideNode, *insideWireNode;
360 RMnode *nonClipNode;
361 RMcolor3D sphereColor={1.0F, 1.0F, 1.0F};
362 float radius = 5.0;
363
364 MyRoot = rmNodeNew(MyRootName,RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
365 rmNodeAddChild(rmRootNode(),MyRoot);
366
367 clipNode = rmNodeNew("clipNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
368 nonClipNode = rmNodeNew("nonClipNode", RM_RENDERPASS_3D,
369 RM_RENDERPASS_OPAQUE);
370
371 rmNodeAddChild(MyRoot, clipNode);
372 rmNodeAddChild(MyRoot, nonClipNode);
373
374 /* build objs */
375 sphereNode = rmNodeNew("sphereNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
376 my_sphere(sphereNode, 0.0F, 0.0F, 0.0F, radius, &sphereColor);
377 rmNodeComputeBoundingBox(sphereNode);
378
379 /* put some stuff inside the sphere */
380 insideNode = rmNodeNew("insideNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
381 buildInsideStuff(insideNode, 0.0F, 0.0F, 0.0F, radius);
382
383 insideWireNode = rmNodeNew("insideWireNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
384 buildInsideStuff(insideWireNode, 0.0F, 0.0F, 0.0F, radius);
385
386 /* clip plane representation */
387 quadNode = rmNodeNew("quadNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
388 my_quad(quadNode, &defaultClipPlanePoint, &defaultClipPlaneNormal, radius);
389 rmNodeComputeBoundingBox(quadNode);
390 rmNodeSetPolygonDrawMode(quadNode, RM_BACK, RM_LINE);
391
392 /* build clip plane */
393 myClipPlane(clipNode, &defaultClipPlanePoint, &defaultClipPlaneNormal);
394
395 /* add stuff to the subtree that will have stuff clipped */
396 rmNodeAddChild(clipNode, sphereNode);
397 rmNodeAddChild(clipNode, insideNode);
398
399 /* add stuff to the subtree where no clipping occurs */
400 rmNodeAddChild(nonClipNode, quadNode);
401
402 rmNodeSetPolygonDrawMode(insideWireNode, RM_FRONT_AND_BACK, RM_LINE);
403 rmNodeSetShader(insideWireNode, RM_SHADER_NOLIGHT);
404 rmNodeAddChild(nonClipNode, insideWireNode);
405
406 {
407 RMcolor4D bgColor={0.2, 0.2, 0.3, 1.0};
408 rmNodeSetSceneBackgroundColor(MyRoot, &bgColor);
409
410 }
411 rmNodeUnionAllBoxes(rmRootNode());
412 }
413
414 void
my_idle_func(RMpipe * p,int ix,int iy)415 my_idle_func(RMpipe *p,
416 int ix,
417 int iy)
418 {
419 RMmatrix m,old;
420 double d,c,s;
421 rmMatrixIdentity(&m);
422 d = RM_DEGREES_TO_RADIANS(1.0);
423 c = cos(d);
424 s = sin(d);
425 m.m[0][0] = m.m[2][2] = c;
426 m.m[0][2] = -s;
427 m.m[2][0] = s;
428
429 rmNodeGetRotateMatrix(MyRoot,&old);
430 rmMatrixMultiply(&old,&m,&old);
431 rmNodeSetRotateMatrix(MyRoot,&old);
432
433 rmFrame(p, rmRootNode());
434
435 /* foil compiler warnings */
436 ix = iy = 0;
437 }
438
pixeltovp(int ipixel,int idim)439 static float pixeltovp(int ipixel, int idim)
440 {
441 float t;
442 t = (float)(ipixel - (idim>>1)) / (float)(idim >> 1);
443 return(t);
444 }
445
446 int
MyStartClipXformFunc(RMpipe * p,int ix,int iy)447 MyStartClipXformFunc(RMpipe *p,
448 int ix,
449 int iy)
450 {
451 int width, height;
452
453 rmPipeGetWindowSize(p,&width, &height);
454
455 /* save the starting position */
456 saveX = pixeltovp(ix,width);
457 saveY = -1.0F * pixeltovp(iy,height);
458 if (rmNodeGetRotateMatrix(quadNode, &startMatrix) == RM_WHACKED)
459 rmMatrixIdentity(&startMatrix);
460
461 rmMatrixInverse(&startMatrix, &quadNodeMatrixInverse);
462
463 if (rmNodeGetRotateMatrix(MyRoot, &MyRootMatrix) == RM_WHACKED)
464 rmMatrixIdentity(&MyRootMatrix);
465
466 rmMatrixInverse(&MyRootMatrix, &MyRootMatrixInverse);
467
468 rmMatrixMultiply(&startMatrix, &MyRootMatrix, &startMatrix);
469 return 1;
470 }
471
472 int
MyClipXformFunc(RMpipe * p,int ix,int iy)473 MyClipXformFunc(RMpipe *p,
474 int ix,
475 int iy)
476 {
477 int width, height;
478 float newX, newY;
479 RMmatrix m;
480 RMvertex3D point,normal;
481
482 rmPipeGetWindowSize(p,&width, &height);
483
484 newX = pixeltovp(ix,width);
485 newY = -1.0F * pixeltovp(iy,height);
486
487 /* compute the quaternion for x1,y1->x2,y2 */
488 rmauxArcBall (&saveX, &saveY, &newX, &newY, &m);
489
490 /* update the rotation matrix on the quad */
491 rmMatrixMultiply(&m, &MyRootMatrixInverse, &m);
492 rmMatrixMultiply(&startMatrix, &m, &m);
493 rmNodeSetRotateMatrix(quadNode, &m);
494
495 /* tweak the clip plane */
496 rmClipPlaneGetPointNormal(&clipPlane, &point, &normal);
497 rmPointMatrixTransform(&defaultClipPlaneNormal, &m, &normal);
498 rmClipPlaneSetPointNormal(&clipPlane, &point, &normal);
499 rmNodeSetSceneClipPlane(clipNode, RM_SCENE_CLIP_PLANE0, &clipPlane);
500
501 /* rerender */
502
503 rmFrame(p, rmRootNode());
504 return(1);
505 }
506
507 void
myinitfunc(RMpipe * p,RMnode * n)508 myinitfunc(RMpipe *p,
509 RMnode *n)
510 {
511 my_build_objs();
512 my_set_scene(n, rmPipeGetChannelFormat(p));
513
514 rmauxSetGeomTransform(MyRoot,p);
515 rmauxSetCamera3DTransform(n, p);
516
517 /* shift+button3 rotates the clip plane around */
518 rmauxSetButtonDownFunc(RM_BUTTON3, RM_SHIFT_MODIFIER, MyStartClipXformFunc);
519 rmauxSetButtonMotionFunc(RM_BUTTON3, RM_SHIFT_MODIFIER, MyClipXformFunc);
520
521 /*
522 * set handler to reset aspect ratio when the window is resized.
523 */
524 rmauxSetResizeFunc(p, n, rmauxDefaultResizeFunc);
525
526 if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE)
527 rmFrame(p, n);
528
529 rmFrame(p, n);
530 }
531
532 #ifdef RM_WIN
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nCmdShow)533 int WINAPI WinMain (HINSTANCE hInstance,
534 HINSTANCE hPrevInstance,
535 LPSTR lpszCmdLine, int nCmdShow)
536 {
537 MSG msg;
538 HWND hWnd;
539 void *fptr;
540 RMpipe *lone_pipe=NULL;
541 RMenum targetPlatform = RM_PIPE_WGL;
542 RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
543 RMenum channelFormat;
544
545 parse_args(__argc,__argv);
546 #else
547 int
548 main(int ac,
549 char *av[])
550 {
551 RMpipe *lone_pipe=NULL;
552 RMenum targetPlatform = RM_PIPE_GLX;
553 RMenum channelFormat;
554 RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
555 void *msg; /* needed for rmauxEventLoop
556 win32/unix API consistency */
557
558 parse_args(ac,av);
559 #endif
560 /*
561 * pick a stereo format:
562 * RM_MONO_CHANNEL - plain old single-view
563 * RM_REDBLUE_STEREO_CHANNEL - left channel in red, right channel in cyan
564 * RM_BLUERED_STEREO_CHANNEL - left in cyan, right in red
565 * RM_MBUF_STEREO_CHANNEL - multibuffered stereo, requires special
566 * hardware.
567 */
568 channelFormat = RM_MONO_CHANNEL;
569
570 /*
571 * first stage of RM initialization.
572 */
573 rmInit();
574
575 /*
576 * create the rendering pipe. this step is required in both
577 * Win32 and X.
578 */
579
580 lone_pipe = rmPipeNew(targetPlatform);
581
582 /*
583 * set the processing mode on the RMpipe. this must happen before
584 * "rmPipeMakeCurrent().
585 */
586 rmPipeSetProcessingMode(lone_pipe, processingMode);
587 rmPipeSetDisplayListEnable(lone_pipe, RM_FALSE);
588
589 #ifdef RM_WIN
590 {
591 /*
592 * Win32: when a window is created, we have to tell windows the
593 * name of the "WndProc," the procedure that gets called by
594 * windows with events (the event loop) (contrast to the X model
595 * where the name of the event loop is not part of the window).
596 * Since we're using RMaux, we know about the event handling
597 * procedure named "rmauxWndProc" and we provide that here.
598 */
599
600 fptr = (void *)(rmauxWndProc);
601 hWnd = rmauxCreateW32Window(lone_pipe,
602 NULL, /* no parent window */
603 20,20,img_width,img_height,"RM for Windows",
604 hInstance,fptr);
605 if (hWnd == 0)
606 exit(-1);
607
608 /*
609 * assign the new window handle to the rendering pipe.
610 */
611 rmPipeSetWindow(lone_pipe,hWnd, img_width, img_height);
612 }
613 #endif
614
615 #ifdef RM_X
616 {
617 Window w;
618
619 w = rmauxCreateXWindow(lone_pipe,
620 (Window)NULL, /* parent window */
621 0,0,img_width,img_height,
622 "RM for X-Windows","icon-title",RM_TRUE);
623 /*
624 * assign the window to the rendering pipe.
625 */
626 rmPipeSetWindow(lone_pipe,w,img_width,img_height);
627
628 }
629 #endif
630
631 /*
632 * specify the name of the "init" function. the "init" function is
633 * mandatory in the Win32 world, and optional in the X world.
634 *
635 * in Win32, we don't want to call RM services until OpenGL is
636 * ready. we can be assured of readiness by using an init function
637 * with RMaux.
638 *
639 * in X, at this point, the window is mapped and OpenGL is ready,
640 * and we could call our init function directly.
641 */
642
643 rmauxSetInitFunc(myinitfunc);
644
645
646 /* uncomment this next line if you want the object to rotate
647 while the user is idle. */
648 /* rmauxSetIdleFunc(lone_pipe, my_idle_func); */
649
650 /*
651 * X-ism: once the window is created and assigned to the
652 * rendering pipe, rmUsePipe makes the OpenGL rendering context
653 * current for the pipe+window combination.
654 *
655 * this step is required for X. in these demo programs, it is not
656 * strictly required by Win32, as the newly created context is made
657 * current as part of the OpenGL initialization sequence.
658 */
659 rmPipeMakeCurrent(lone_pipe);
660
661 /*
662 * set key handler function so this prog will exit on "q" key.
663 */
664 rmauxSetKeyFunc(lone_pipe, rmauxDefaultKeyFunc);
665
666 rmauxEventLoop(lone_pipe,rmRootNode(), &msg);
667
668 rmPipeDelete(lone_pipe);
669 rmFinish();
670
671 #ifdef RM_WIN
672 return( msg.wParam );
673 #else
674 return(1);
675 #endif
676
677 }
678