1 /*
2  * Copyright (C) 1997-2005, R3vis Corporation.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
18  *
19  * Original Contributor:
20  *   Wes Bethel, R3vis Corporation, Marin County, California
21  * Additional Contributor(s):
22  *
23  * The OpenRM project is located at http://openrm.sourceforge.net/.
24  */
25 /*
26  * $Id: rmbfuncs.c,v 1.14 2005/09/12 04:05:34 wes Exp $
27  * Version: $Name: OpenRM-1-6-0-2-RC2 $
28  * $Revision: 1.14 $
29  * $Log: rmbfuncs.c,v $
30  * Revision 1.14  2005/09/12 04:05:34  wes
31  * Minor documentation updates.
32  *
33  * Revision 1.13  2005/06/08 18:33:02  wes
34  * Code cleanups to eliminate compiler warnings.
35  *
36  * Revision 1.12  2005/04/27 03:36:00  wes
37  * Attempting to reduce idle timeout in Win32
38  *
39  * Revision 1.11  2005/02/19 16:08:45  wes
40  * Distro sync and consolidation.
41  *
42  * Revision 1.10  2005/01/23 17:11:49  wes
43  * Copyright update to 2005.
44  *
45  * Revision 1.9  2004/03/30 14:13:37  wes
46  * *** empty log message ***
47  *
48  * Revision 1.8  2004/03/18 15:51:13  wes
49  * Minor changes to switch statements to avoid compile warning on SGI
50  * about "statement not reached" when there is a return inside a case.
51  *
52  * Revision 1.7  2004/01/19 17:10:24  wes
53  * Minor docs changes.
54  *
55  * Revision 1.6  2004/01/17 04:08:17  wes
56  * Updated copyright line for 2004. Tuning of auto-spin code.
57  *
58  * Revision 1.5  2003/12/12 00:36:45  wes
59  * Changed timeout parameter on the Win32 routine SetTime from 10 to 1. THe
60  * intent is to reduce the idle timer from 10ms to 1ms. After the change,
61  * I can't tell any difference, but it didn't seem to break anything. We need
62  * to test on a CPU with a higher clock rate to see if there's any difference
63  * in performance. This change affects the frequency that the user's
64  * idle callback rmaux function will be invoked.
65  *
66  * Revision 1.4  2003/10/03 19:18:01  wes
67  * Add support for auto-spin in both Win32 and X11.
68  *
69  * Revision 1.3  2003/02/18 15:41:37  wes
70  * Added a bit of documentation to rmauxSetKeyFunc.
71  *
72  * Revision 1.2  2003/02/02 02:07:18  wes
73  * Updated copyright to 2003.
74  *
75  * Revision 1.1.1.1  2003/01/28 02:15:23  wes
76  * Manual rebuild of rm150 repository.
77  *
78  * Revision 1.14  2003/01/16 22:21:18  wes
79  * Updated all source files to reflect new organization of header files:
80  * all header files formerly located in include/rmaux, include/rmi, include/rmv
81  * are now located in include/rm.
82  *
83  * Revision 1.13  2002/06/17 01:15:18  wes
84  * Replaced use of rmSubtreeFrame with rmFrame for default rendering
85  * operations. Added conditionals to check for NULL callbacks
86  * on event handling calls (button push, motion and release events).
87  *
88  * Revision 1.12  2002/04/30 19:37:59  wes
89  * Added code to Win32 event handler to invoke a caller-supplied
90  * key func callback.
91  *
92  * Revision 1.11  2001/10/14 23:52:42  wes
93  * Added button4 and button5 due to crashes reported by some users with
94  * mice with scroll wheels, and other funny mice with lots of buttons. Note
95  * that buttons 4 and 5 are accessible only under X11; Win32 provides for
96  * only 3 buttons in their API.
97  *
98  * Added a bit of code to the Win32 event processing code to more cleanly
99  * delimit those events consumed by the RMaux event loop and those to be
100  * consumed by Windows.
101  *
102  * Revision 1.10  2001/07/15 17:20:57  wes
103  * Added an RMnode * parameter to the rmauxInitFunc.
104  *
105  * Revision 1.9  2001/06/03 20:53:47  wes
106  * Added new routines for establishing callback handlers for resize
107  * and key handler events.
108  *
109  * Revision 1.8  2001/03/31 17:09:31  wes
110  * v1.4.0-alpha2 checkin.
111  *
112  * Revision 1.7  2000/12/03 22:36:15  wes
113  * First steps towards thread safety.
114  *
115  * Revision 1.6  2000/10/03 11:38:26  wes
116  * In rmauxEventLoop() [X11], upon entry and before entering
117  * the main event loop, we first remove any KeyRelease events
118  * from the event queue. There was a problem that first appeared
119  * in XF864.0.1 in which spurious KeyRelease events would sometimes
120  * be present in the event queue for the OpenGL gfx window.
121  *
122  * Revision 1.5  2000/08/28 01:31:40  wes
123  * Minor tweaks to key latching stuff.
124  *
125  * Revision 1.3  2000/04/20 16:22:16  wes
126  * JDB modifications: additional documentation, code rearranging.
127  *
128  * Revision 1.2  2000/04/17 00:06:51  wes
129  * Numerous documentation updates and code reorganization courtesy of jdb.
130  *
131  * Revision 1.1.1.1  2000/02/28 21:29:40  wes
132  * OpenRM 1.2 Checkin
133  *
134  * Revision 1.1.1.1  2000/02/28 17:18:48  wes
135  * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
136  *
137  */
138 
139 /*
140  * current issues 8/1999
141  * 1. rmauxEventLoop under Win32 does not exit upon a non-modifier
142  *    keypress.
143  */
144 
145 /*
146  * MODIFICATIONS:
147  *
148  * 1. added simple user key callback
149  * 2. added user window resize callback
150  * 3. added view plane translation to UI
151  * 4. replaced isometric scaling with camera dolly in UI
152  * 5. replaced trackball with ArcBall implementation in UI
153  * 6. substituted colored X11 cursors for emphasizing operations in UI
154  * 7. cleaned up some variable and function names, slimmed rmaux state, added docs
155  * 8. made button function arrays `static extern' to make them private
156  *
157  * 08/16/2000 jdb
158  *
159  * added key and resize default callbacks and API calls for setting rmaux state
160  * with users functions for these callbacks
161  *
162  * 02/16/01 jdb
163  */
164 
165 #include <rm/rm.h>
166 #include <rm/rmaux.h>
167 #include "../rm/rmprivat.h"
168 
169 #ifdef RM_X
170 #include <X11/cursorfont.h>
171 #endif
172 
173 #if 1
174 #define private_rmauxSpinThreshold(x1,y1,x2,y2,t) (((((x2)-(x1))*((x2)-(x1))+((y2)-(y1))*((y2)-(y1))) - (t)) > 0 ? 1 : -1)
175 #else
176 int
private_rmauxSpinThreshold(float x1,float y1,float x2,float y2,float t)177 private_rmauxSpinThreshold(float x1,
178 			   float y1,
179 			   float x2,
180 			   float y2,
181 			   float t)
182 {
183 
184     float a = (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1);
185 #if 1
186     {
187 	char buf[256];
188 	sprintf(buf," p1/p2 = (%g,%g) (%g,%g) \n thresh = %g, v = %g \n", x1, y1, x2, y2, t, a);
189 	rmNotice(buf);
190     }
191 #endif
192     if ((a - t) > 0)
193 	return 1;
194     else
195 	return -1;
196 }
197 #endif
198 
199 
200 /* rmaux state - NOT thread-safe! */
201 static int    (*staticUserIdleFunc)(RMpipe *, int, int) = NULL;
202 static void   (*staticUserInitFunc)(RMpipe *, RMnode *) = NULL;
203 static int    (*staticUserKeyFunc)(RMpipe *, char, KeySym) = NULL;
204 static int    (*staticUserResizeFunc)(RMpipe *,RMnode *, int, int) = NULL;
205 static void   (*renderfunc)(RMpipe *, RMnode *) = rmFrame;
206 static float    x, y;       /* stored pointer location */
207 static RMnode  *camera3DTransformTarget;  /* UI points to target node for interaction */
208 static RMmatrix ui_pose;    /* UI stores initial target node pose for computation */
209 static RMnode  *geomTransformTarget;
210 static RMmatrix saveScaleMatrix;
211 static float   xscale_delta, yscale_delta;
212 static RMnode *static_sceneGraphHandle=NULL;
213 static RMnode *static_resizeCameraNode=NULL;
214 
215 static RMenum  spinModeEnabled=RM_FALSE;
216 static void    (*spinCallbackFunc)(void) = NULL;
217 static void 	private_rmauxDoSpinCallback(void);
218 static RMmatrix spinMatrix;
219 static float   lastBX1, lastBY1, lastBX2, lastBY2;
220 static float   staticSpinThreshold = RMAUX_DEFAULT_SPIN_THRESHOLD;
221 
222 #ifdef RM_X
223 static Cursor   rotate_cursor, translate_cursor, dolly_cursor, scale_cursor;
224 #endif
225 
226 /* button function callbacks (static extern prevents raw modification) */
227 static int (*rmauxUserButtonDownFuncs[RM_NUM_BUTTON_MODIFIERS][RM_NUM_BUTTONS + 1]) RMAUX_BUTTON_FUNC_PARMS();
228 static int (*rmauxUserButtonUpFuncs[RM_NUM_BUTTON_MODIFIERS][RM_NUM_BUTTONS + 1]) RMAUX_BUTTON_FUNC_PARMS();
229 static int (*rmauxUserButtonMotionFuncs[RM_NUM_BUTTON_MODIFIERS][RM_NUM_BUTTONS + 1]) RMAUX_BUTTON_FUNC_PARMS();
230 
231 /* PRIVATE declarations */
232 void rmauxInvokeRenderFunc (RMpipe *p, RMnode *n);
233 int  rmauxB1DownFunc RMAUX_BUTTON_FUNC_PARMS();
234 int  rmauxB1UpFunc RMAUX_BUTTON_FUNC_PARMS();
235 int  rmauxB1MotionFunc RMAUX_BUTTON_FUNC_PARMS();
236 int  rmauxB2DownFunc RMAUX_BUTTON_FUNC_PARMS();
237 int  rmauxB2UpFunc RMAUX_BUTTON_FUNC_PARMS();
238 int  rmauxB2MotionFunc RMAUX_BUTTON_FUNC_PARMS();
239 int  rmauxB3DownFunc RMAUX_BUTTON_FUNC_PARMS();
240 int  rmauxB3UpFunc RMAUX_BUTTON_FUNC_PARMS();
241 int  rmauxB3MotionFunc RMAUX_BUTTON_FUNC_PARMS();
242 int  rmauxShiftB2DownFunc RMAUX_BUTTON_FUNC_PARMS();
243 int  rmauxShiftB2UpFunc RMAUX_BUTTON_FUNC_PARMS();
244 int  rmauxShiftB2MotionFunc RMAUX_BUTTON_FUNC_PARMS();
245 
246 int  private_rmauxButtonNumToIndex (int xbutton_num);
247 int  private_rmauxModifierToIndex (int state);
248 
249 RMnode *private_rmauxGetCurrentSceneGraph(void);
250 void private_rmauxSetCurrentSceneGraph(RMnode *n);
251 
252 RMnode *private_rmauxGetCameraResizeNode(void);
253 void private_rmauxSetCameraResizeNode(RMnode *n);
254 
255 /* viewport [0..(width-1)] X [0..(height-1)] --> NDC [-1,1] X [-1,1] */
256 #define pixeltovp(p,d) ((float)((p) - ((d) >> 1)) / (float)((d) >> 1))
257 
258 /*
259  * ----------------------------------------------------
260  * @Name rmauxEventLoop
261  @pstart
262  void rmauxEventLoop (RMpipe *currentPipe,
263                       RMnode *subTreeToDraw,
264                       void *vmsg)
265  @pend
266 
267  @astart
268  RMpipe *currentPipe - (input) a handle to an RMpipe object. The
269     RMpipe must be fully configured (init'ed, and has a valid opened
270     window assigned, and been the victim of rmPipeMakeCurrent()) prior
271     to invoking the event handler.
272 
273  RMnode *subTreeToDraw - (input) a handle to an RMnode. This node is
274     considered to be the root of a subtree that will be rendered onto
275     "currentPipe" whenever a frame is to be rendered.
276 
277  void *vmsg - X11 users - specify NULL for this parameter.  Win32
278     users: all WinMain procedures must return (msg.wParam) to Windows
279     so it can gracefully exit. Pass a handle to a MSG object in to
280     rmauxEventLoop (please refer to the demo programs for sample
281     code. (output, return)
282  @aend
283 
284  @dstart
285 
286  This is the main rmaux event handler loop for both Win32 and X11.  In
287  the source code, there are actually two different routines that are
288  conditionally compiled, since Win32 and X11 are so different.
289  However, from the outside, they exhibit (mostly) similar behavior.
290 
291  Events are processed until some action handler returns a zero
292  status.  The RM demonstration programs install a key handler function
293  (rmauxSetKeyFunc(pipe, rmauxDefaultKeyFunc)) that will cause applications
294  to terminate when the "q" key is pressed.
295 
296  The default rendering function will be invoked an Expose (X11) or WM_PAINT (Win32)
297  event is encountered. Additionally, rmauxUI() defines that frames will
298  be rendered when other types of input events are encountered. The
299  RMnode * parameter subTreeToDraw defines the root of the scene graph
300  that will be rendered during all frame renderings within the rmaux*
301  family of routines. Note the distinction between the scene graph root
302  (subTreeToDraw) and the RMnode handle used for interactive transformations,
303  which is set with rmauxUI(). The RMnode handles to each of these routines
304  may point to different RMnodes.
305 
306  @dend
307  * ----------------------------------------------------
308  */
309 #ifdef RM_WIN /* W32 event loop */
310 
311 static RMpipe *staticPipe=NULL; /* hack! tmp! wes */
312 
313 void
rmauxEventLoop(RMpipe * win_current_pipe,RMnode * subTreeToDraw,void * vmsg)314 rmauxEventLoop (RMpipe *win_current_pipe,
315 		RMnode *subTreeToDraw,
316 	        void *vmsg)
317 {
318     MSG *msg;
319 
320     private_rmauxSetCurrentSceneGraph(subTreeToDraw);
321 
322     msg = (MSG *)vmsg;
323     ShowWindow(win_current_pipe->hwnd, 1);
324 
325 /*    staticPipe = win_current_pipe; */
326 
327     if (staticUserInitFunc != NULL)
328     {
329         (*staticUserInitFunc)(win_current_pipe, subTreeToDraw);
330 	staticUserInitFunc = NULL;
331     }
332     UpdateWindow(win_current_pipe->hwnd);
333 
334     /* message structure, handle of window receiving the message,
335      * lowest message id to examine, highest message id to examine
336      */
337     while (GetMessage(msg, NULL, 0, 0))
338     {
339         TranslateMessage(msg); /* translates messages */
340         DispatchMessage(msg);  /* dispatches messages */
341     }
342 }
343 
344 
345 /*
346  * the following are used to hold pointer (x,y) coordinates that
347  * will survive between calls to the WndProc. we use int *'s so
348  * this code will be thread safe.
349  */
350 static int *winPointerXY = NULL;
351 
rmauxWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)352 LONG WINAPI rmauxWndProc(HWND hWnd,
353 			 UINT msg,
354 			 WPARAM wParam,
355 			 LPARAM lParam)
356 {
357     static int   which_button_down = -1;
358     static int   which_modifier_index = -1;
359     static HDC   hDC;
360     static HGLRC hRC;
361     PAINTSTRUCT  ps;
362     GLsizei      glnWidth, glnHeight;
363     RMpipe      *win_current_pipe=NULL;
364     int    stat = 0;
365     int          consumedEvent = 0;
366 
367     /*
368      * Windows weirdnesses (2/2000):
369      *
370      * the routine that processes events (this routine) is not called
371      * directly by the app, but by the window system. we need the RMpipe
372      * handle to pass back to callbacks, so we cheat and use a static
373      * variable inside RM. there's probably a way to wedge this into
374      * the parmameters that are passed along from rmauxEventLoop
375      * (win32 version) down to this routine, and this will be used
376      * in the future.
377      *
378      * This routine is NOT thread safe because of win_current_pipe.
379      *
380      * TODO: HOW to end the WndProc/event loop w/o Destroying the Window?
381 
382      * 4/20/02 - this routine was updated to unify keypress handling on both
383      * X11 and Win32 platforms. The same API is used in both places, but the
384      * KeySym type is artificially defined on Win32 in order to coerce API
385      * consistency across platforms. Presently, the KeySym "code" parameter
386      * is not used on Win32. Only printable ASCII chars will trigger a call to
387      * the user-defined key-handling function on Win32; refer to your Win32
388      * developer documentation to learn more about which keys actually trigger
389      * an WM_CHAR event, which in turn initiates a call to the user-supplied
390      * key-handling callback.
391      */
392 
393     if (staticPipe != NULL)
394 	win_current_pipe = staticPipe;
395 
396     /* malloc memory to hold the static variable used to hold the pointer (x,y) position */
397     if (winPointerXY == NULL)
398     {
399         winPointerXY = (int *)malloc(sizeof(int) * 2);
400 	winPointerXY[0] = winPointerXY[1] = 0;
401     }
402     switch (msg)
403        {
404        case WM_TIMER:
405 	   stat = 1;
406 	   if (staticUserIdleFunc != NULL)
407 	       stat = (*staticUserIdleFunc)(win_current_pipe, winPointerXY[0], winPointerXY[1]);
408 	   if (spinCallbackFunc != NULL)
409 	   {
410 	       (*spinCallbackFunc)();
411 	       rmauxInvokeRenderFunc(win_current_pipe,
412 				     private_rmauxGetCurrentSceneGraph());
413 	       stat = 1;
414 	   }
415 	   if (stat <= 0)
416 	       DestroyWindow(hWnd);
417 
418 	  consumedEvent = 1;
419 	  break;
420 
421        case WM_CREATE:
422 	  {
423 	     /*
424 	      * assumption: when the window is created, the event
425 	      * loop routine has not yet been called. therefore, we need
426 	      * to grab the current pipe structure and stuff the window
427 	      * handle into it's field. note that later in the app code,
428 	      * we will explicitly call rmwPipeSetWindow for the purposes
429 	      * of syntactical consistency between the X and Win models,
430 	      * but in fact rmwPipeSetWindow is not *required* because
431 	      * we do it here.
432 	      */
433 
434 	     int     width, height;
435 	     RMpipe *p;
436 
437 	     width = (int) LOWORD (lParam);
438 	     height = (int) HIWORD (lParam);
439 
440 	     /* grap the RMpipe * passed in through the rmwCreateWindow call. */
441 	     p = (RMpipe *)(((LPCREATESTRUCT)lParam)->lpCreateParams);
442 
443 	     /* make it the current RMpipe for this event loop */
444 
445 	     win_current_pipe = staticPipe = p;
446 
447 	     /* Select a pixel format and then  create a rendering context from it */
448 	     if (win_current_pipe != NULL)
449 	     {
450 #if 0
451 		 hDC = GetDC(hWnd);
452 		 p->hdc = hDC;
453 
454 		 setupPixelFormat(hDC, 16, 0); /* 16 bit zbuffer? */
455 		 setupPalette(hDC);
456 		 p->hRC = wglCreateContext(hDC);
457 		 rmPipeMakeCurrent(win_current_pipe);
458 		 /* build internal display lists for quadrics objects */
459 		 private_rmInitQuadrics(win_current_pipe->cache);
460 #endif
461 		}
462 	     consumedEvent = 1;
463 	     break;
464 	  }
465 
466        case WM_SIZE:
467 	  {
468 	     /*
469 	      * Redefine the viewing volume and viewport when the window size changes
470 	      * Make the RC current since we're going to make an OpenGL call here...
471 	      * get the new size of the client window
472 	      * note that we size according to the height,
473 	      * not the smaller of the height or width.
474 	      */
475 
476 	     glnWidth = (GLsizei) LOWORD (lParam);
477 	     glnHeight = (GLsizei) HIWORD (lParam);
478 
479 	     /*
480 	      * the following conditional checks for those events generated
481 	      * by win32 when a window is minimized, and its resulting width
482 	      * and/or height are set to zero. We avoid invoking the resize
483 	      * function if this is the case.
484 	      */
485 	     if ((glnWidth == 0) || (glnHeight == 0))
486 		 break;
487 
488 	     rmPipeSetWindowSize(win_current_pipe, (int)glnWidth, (int)glnHeight);
489 	     if (staticUserResizeFunc != NULL)
490 	     {
491 		 if (staticUserResizeFunc(win_current_pipe, private_rmauxGetCameraResizeNode(), glnWidth, glnHeight) != RM_CHILL)
492 		     rmError("rmauxEventLoop() error - abnormal return status from user resizing function.");
493 		 else
494 		     rmauxInvokeRenderFunc(win_current_pipe,
495 					   private_rmauxGetCurrentSceneGraph()); /* draw resized window */
496 	     }
497 
498 	     consumedEvent = 1;
499 	     break;
500 	  }
501 
502        case WM_PAINT:
503 	  rmauxInvokeRenderFunc(win_current_pipe,
504 				private_rmauxGetCurrentSceneGraph());
505 	  ValidateRect(hWnd, NULL);
506 	  consumedEvent = 1;
507 	  break;
508 
509        case WM_DESTROY: /* Clean up and terminate */
510 	  wglDeleteContext( win_current_pipe->hRC );
511 	  PostQuitMessage( 0 );
512 	  consumedEvent = 1;
513 	  break;
514 
515 /********		   rmWarning(" need to add code to check and set buttons other than left, middle and up \n"); */
516 		   break;
517        case WM_LBUTTONDOWN:	/* button 1 */
518        case WM_RBUTTONDOWN:	/* button 3 */
519        case WM_MBUTTONDOWN:	/* button 2 */
520 	  {
521 	     int ix, iy, rstat, bindex, mod_index;
522 
523 	     winPointerXY[0] = ix = (int)LOWORD(lParam); /* pointer location */
524 	     winPointerXY[1] = iy = (int)HIWORD(lParam);
525 
526 	     /* set mouse button */
527 	     switch (msg)
528 		{
529 		case WM_LBUTTONDOWN:
530 		   bindex = Button1;
531 		   break;
532 
533 		case WM_MBUTTONDOWN:
534 		   bindex = Button2;
535 		   break;
536 		case WM_RBUTTONDOWN:
537 		   bindex = Button3;
538 		   break;
539 
540 		default: /* bogus mouse button */
541 		   break;
542 		}
543 
544 	     which_button_down = bindex = private_rmauxButtonNumToIndex(bindex);
545 
546 	     mod_index = 0; /* determine modifiers in effect when button goes down */
547 	     if (wParam & MK_CONTROL)
548 		mod_index |= RM_CONTROL_MODIFIER;
549 	     if (wParam & MK_SHIFT)
550 		mod_index |= RM_SHIFT_MODIFIER;
551 	     which_modifier_index = mod_index = private_rmauxModifierToIndex(mod_index);
552 
553 	     if (rmauxUserButtonDownFuncs[mod_index][bindex] != NULL)
554 		 rstat = (*rmauxUserButtonDownFuncs[mod_index][bindex])(win_current_pipe, ix, iy);
555 	     else
556 		 rstat = 1;
557 	     consumedEvent = 1;
558 	     break;
559 	  }
560 
561        case WM_MOUSEMOVE:
562 	  {
563 	     int ix, iy, rstat;
564 
565 	     winPointerXY[0] = ix = (int)LOWORD(lParam);
566 	     winPointerXY[1] = iy = (int)HIWORD(lParam);
567 
568 	     if (which_button_down != -1)
569 		{
570 		    if (rmauxUserButtonMotionFuncs[which_modifier_index][which_button_down] != NULL)
571 			rstat = (*rmauxUserButtonMotionFuncs[which_modifier_index][which_button_down])(win_current_pipe, ix, iy);
572 		    else
573 			rstat = 1;
574 
575 		   consumedEvent = 1;
576 #if 0
577 		   /* this code produces failure on some Win98 systems, and
578 		    is being temporarily removed until this issue is
579 		    resolved. 10/9/2001. wbethel */
580 
581 		   /* test removing extraneous WM_MOUSEMOVE events */
582 		   {
583 		      LPMSG lpMsg;
584 		      UINT  filterFirst, filterLast;
585 		      UINT  removeFlags = PM_REMOVE;
586 
587 		      filterFirst = filterLast = WM_MOUSEMOVE;
588 
589 		      while (PeekMessage(lpMsg, hWnd, filterFirst, filterLast, removeFlags))
590 			 ; /* just remove 'em all */
591 		   }
592 #endif
593 		}
594 	     break;
595 	  }
596 
597        case WM_LBUTTONUP:
598        case WM_MBUTTONUP:
599        case WM_RBUTTONUP:
600 	  {
601 	     int ix, iy;
602 	     int rstat, bindex, mod_index;
603 
604 	     winPointerXY[0] = ix = (int)LOWORD(lParam);
605 	     winPointerXY[1] = iy = (int)HIWORD(lParam);
606 
607 	     if (msg == WM_LBUTTONUP)
608 		bindex = Button1;
609 	     else if (msg == WM_MBUTTONUP)
610 		bindex = Button2;
611 	     else if (msg == WM_RBUTTONUP)
612 		bindex = Button3;
613 	     else
614 		 rmWarning(" need to add code to check and set buttons other than left, middle and up \n");
615 
616 	     which_button_down = bindex = private_rmauxButtonNumToIndex(bindex);
617 
618 	     mod_index = 0;		/* determine modifiers in effect when button goes down */
619 	     if (wParam & MK_CONTROL)
620 		mod_index |= RM_CONTROL_MODIFIER;
621 	     if (wParam & MK_SHIFT)
622 		mod_index |= RM_SHIFT_MODIFIER;
623 	     which_modifier_index = mod_index = private_rmauxModifierToIndex(mod_index);
624 	     if (rmauxUserButtonUpFuncs[which_modifier_index][which_button_down] != NULL)
625 		 rstat = (*rmauxUserButtonUpFuncs[which_modifier_index][which_button_down])(win_current_pipe, ix, iy);
626 	     else
627 		 rstat = 1;
628 	     which_button_down = -1;
629 	     consumedEvent = 1;
630 	     break;
631 	  }
632       case WM_CHAR:
633 	  {
634 	      if (staticUserKeyFunc != NULL)
635 	      {
636 		  consumedEvent = 1;
637 		  /*
638 		   * 4/20/02 - note the zero 3rd parm is a dummy. this code
639 		   * will invoke the user-supplied keypress callback, if
640 		   * present.
641 		   */
642 		  if (staticUserKeyFunc(win_current_pipe, (char)(wParam), 0) != RM_CHILL)
643 		  {
644 		      DestroyWindow(hWnd);
645 		  }
646 	      }
647 	      break;
648 	  }
649       }
650 
651     if (consumedEvent != 0)
652 	return 0;
653     else
654 	return DefWindowProc(hWnd, msg, wParam, lParam);
655 }
656 
rmauxOffscreenWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)657 LONG WINAPI rmauxOffscreenWndProc(HWND hWnd,
658 				  UINT message,
659 				  WPARAM wParam,
660 				  LPARAM lParam)
661 {
662     switch (message) {
663     case WM_CREATE:
664 	return 0;
665     case WM_DESTROY:
666 	PostQuitMessage(0);
667 	return 0;
668     case WM_SIZE:
669     case WM_PALETTECHANGED:
670 	break;
671     case WM_QUERYNEWPALETTE:
672 	break;
673     case WM_PAINT:
674 	break;
675     case WM_CHAR:
676 	break;
677     default:
678 	break;
679     }
680 
681     /* Deal with any unprocessed messages */
682     return DefWindowProc(hWnd, message, wParam, lParam);
683 }
684 #endif /* RM_WIN */
685 
686 #ifdef RM_X /* X11 event loop (documentation above) */
687 void
rmauxEventLoop(RMpipe * pipe,RMnode * subTree,void * unused)688 rmauxEventLoop (RMpipe *pipe,
689 		RMnode *subTree,
690 	        void *unused)
691 {
692     int    button_index = -1, modifier_index = -1;
693     XEvent event;
694     long   mask;
695     int    stat = 1;
696     int    event_stat;
697     int    pointerX = 0, pointerY = 0;
698 
699     private_rmauxSetCurrentSceneGraph(subTree);
700 
701     /* clear out any lingering keyrelease & keypress events on the display window */
702     mask = KeyPress | KeyRelease;
703     XSelectInput(rmxPipeGetDisplay(pipe), rmPipeGetWindow(pipe), mask);
704 
705     XSync(rmxPipeGetDisplay(pipe), 0);
706     while (XCheckWindowEvent(rmxPipeGetDisplay(pipe),
707 			     rmPipeGetWindow(pipe), mask,  &event) == True)
708 	fprintf(stderr," removing errant Keypress or KeyRelease event \n");
709 
710     if (staticUserInitFunc != NULL)
711     {
712         (*staticUserInitFunc)(pipe, subTree);
713         staticUserInitFunc = NULL;
714     }
715 
716     mask = RM_AUX_XWINDOW_EVENT_MASK;
717     XSelectInput(rmxPipeGetDisplay(pipe), rmPipeGetWindow(pipe), mask);
718 
719     while (stat > 0)
720     {
721         if ((staticUserIdleFunc != NULL) || (spinCallbackFunc != NULL))
722 	   event_stat = XCheckWindowEvent(rmxPipeGetDisplay(pipe), rmPipeGetWindow(pipe), mask, &event);
723 	else
724 	   event_stat = XWindowEvent(rmxPipeGetDisplay(pipe), rmPipeGetWindow(pipe), mask, &event);
725 
726 	if ((event_stat == False) && ((staticUserIdleFunc != NULL) || (spinCallbackFunc != NULL)))
727 	{
728 	    if (staticUserIdleFunc != NULL)
729 		stat = (*staticUserIdleFunc)(pipe, pointerX, pointerY);
730 
731 	    if (spinCallbackFunc != NULL)
732 	    {
733 		(*spinCallbackFunc)();
734 		rmauxInvokeRenderFunc(pipe, subTree);
735 	    }
736 	}
737 	else
738 	{
739 	    switch(event.type)
740 		{
741 		case Expose:
742 		    if (event.xexpose.count == 0)
743 		    {
744 #if 0
745 			/* debug */
746 			printf(" doing a render on Expose, count=0 \n");
747 #endif
748 			rmauxInvokeRenderFunc(pipe, subTree);
749 		    }
750 #if 0
751 		    /* debug */
752 		    else
753 			printf(" Expose event, count != 0 \n");
754 #endif
755 		    break;
756 
757 		case ButtonPress:
758 		    {
759 			button_index = private_rmauxButtonNumToIndex(event.xbutton.button);
760 			modifier_index = private_rmauxModifierToIndex(event.xbutton.state);
761 			pointerX = event.xbutton.x;
762 			pointerY = event.xbutton.y;
763 
764 			/* added 6/10/02 to avoid user-induced crash */
765 			if (rmauxUserButtonDownFuncs[modifier_index][button_index] != NULL)
766 			  stat = (*rmauxUserButtonDownFuncs[modifier_index][button_index])(pipe, pointerX, pointerY);
767 			break;
768 		    }
769 
770 		case ButtonRelease:
771 		    {
772 			button_index = private_rmauxButtonNumToIndex(event.xbutton.button);
773 			modifier_index = private_rmauxModifierToIndex(event.xbutton.state);
774 			pointerX = event.xbutton.x;
775 			pointerY = event.xbutton.y;
776 
777 			/* added 6/10/02 to avoid user-induced crash */
778 			if (rmauxUserButtonUpFuncs[modifier_index][button_index] != NULL)
779 			  stat = (*rmauxUserButtonUpFuncs[modifier_index][button_index])(pipe, pointerX, pointerY);
780 
781 			button_index = modifier_index = -1;
782 			break;
783 		    }
784 
785 		case MotionNotify:
786 		    {
787 			/*
788 			 * we'll assume that "button_index" and "modifier_index"
789 			 * were set by the buttonpress event, and that they
790 			 * are still valid. sometimes these values aren't properly
791 			 * set on motion events.  the UI metaphor is that whatever
792 			 * these values are button index, modifier index) is what
793 			 * they are for all subsequent motion events until a release
794 			 * event.
795 			 */
796 
797 /*			pointerX = event.xmotion.x;
798 			pointerY = event.xmotion.y; */
799 			pointerX = event.xbutton.x;
800 			pointerY = event.xbutton.y;
801 
802 			/* check for invalid state on button & modifier tags */
803 			if ((modifier_index == -1) || (button_index == -1))
804 			    break;	/* bail if they're invalid - this implies just pointer motion w/no button press */
805 
806 			/* call the routine which processes motion events. */
807 			/* added 6/10/02 to avoid user-induced crash */
808 			if (rmauxUserButtonMotionFuncs[modifier_index][button_index] != NULL)
809 			  stat = (*rmauxUserButtonMotionFuncs[modifier_index][button_index])(pipe, pointerX, pointerY);
810 
811 			/*
812 			 * while ButtonMotion events remain, peel them off. the
813 			 * problem we're addressing here is that ButtonMotion
814 			 * events can pile up when the renderer (or whatever action
815 			 * is mapped to ButtonMotion) is slower than the user. if
816 			 * we don't peel off "excess" events and throw them away,
817 			 * the renderer redraws for all the intermediate positions.
818 			 * the user probably wants the renderer to draw using
819 			 * the most recent pointer position, not all the intermediate
820 			 * positions.
821 			 */
822 
823 			while (XCheckMaskEvent(rmxPipeGetDisplay(pipe), ButtonMotionMask, &event) == True)
824 			    ;
825 
826 			break;
827 
828 		    }
829 
830 		case ConfigureNotify:
831 		    {
832 			/*
833 			 * when we get notification of a window size change, we
834 			 * simply set the size attribute of the window pipe. we
835 			 * make no assumption about modification of the aspect
836 			 * ratio of cameras within this window, if any, however,
837 			 * a non-NULL user-defined callback will be called to
838 			 * resize the window as required.
839 			 */
840 
841 			int new_width, new_height;
842 
843 			new_width = event.xconfigure.width;
844 			new_height = event.xconfigure.height;
845 			rmPipeSetWindowSize(pipe, new_width, new_height);
846 
847 			/* try to resize the window */
848 			if (staticUserResizeFunc != NULL)
849 			{
850 			    if (staticUserResizeFunc(pipe, private_rmauxGetCameraResizeNode(), new_width, new_height) != RM_CHILL)
851 				rmError("rmauxEventLoop() error - abnormal return status from user resizing function.");
852 			    else
853 				rmauxInvokeRenderFunc(pipe, subTree); /* draw resized window */
854 			}
855 #if 0
856 			/* debug */
857 			printf(" rendering because of configurenotify \n");
858 #endif
859 			rmauxInvokeRenderFunc(pipe, subTree);
860 			break;
861 		    }
862 
863 		case KeyPress:
864 		case KeyRelease:
865 		    {
866 			/*
867 			 * grab both key presses and releases in order to latch
868 			 * keys for individual application actions
869 			 *
870 			 * keys are NOT latched when no key handler callback
871 			 * exists. when a keyhandler does not exist, the
872 			 * event loop exits when any non-modifier key
873 			 * is pressed.
874 			 *
875 			 * 8/27/2000 - this code needs more thorough testing.
876 			 *
877 			 * 02/11/01 - added rmaux state key function jdb
878 			 */
879 
880 			static char    key, latch = 0;
881 			static KeySym  code;
882 			char           buf_return[32];
883 			int            len;
884 			KeySym         keysym_ret;
885 			XComposeStatus status;
886 
887 			len = XLookupString(&(event.xkey), buf_return, 32, &keysym_ret, &status);
888 
889 			/* check to see if the key is one of the modifiers */
890 			if (((event.type == KeyPress) || (event.type == KeyRelease)) && ((keysym_ret == XK_Shift_L) || (keysym_ret == XK_Shift_R) ||
891 							 (keysym_ret == XK_Control_L) || (keysym_ret == XK_Control_R)))
892 			    stat = 1;
893 			else /* not a modifier - pass latched key press back to non-NULL key handler */
894 			{
895 			    /* release latched key */
896 			    if ((latch) && (event.type == KeyRelease) && (staticUserKeyFunc != NULL))
897 			    {
898 				if ((*buf_return == key) || (keysym_ret == code))
899 				{
900 				    if ((staticUserKeyFunc(pipe, *buf_return, keysym_ret)) != RM_CHILL)
901 				    {
902 					stat = 0; /* RM_WHACKED exits event loop */
903 #if 0
904 				/* debug code */
905 					fprintf(stderr," exiting due to keypress & latch \n");
906 #endif
907 				    }
908 				    else
909 				    {
910 					latch = 0;
911 					stat = 1;
912 				    }
913 				} /* else oops! */
914 			    }
915 			    else /* latch new key press */
916 			    {
917 				if ((event.type == KeyPress) && (staticUserKeyFunc != NULL))
918 				{
919 				    key = *buf_return;
920 				    code = keysym_ret;
921 				    latch = 1;
922 				    stat = 1;
923 				}
924 #if 0
925 				else
926 				{
927 				/* this code added because XF86 is stupidly
928 				 fabricating a <cr> !!! */
929 				    if ((int)buf_return[0] != 13)
930 				    {
931 					stat = 0;
932 #if 0
933 					/* debug */
934 					fprintf(stderr," exiting due to keypress <%s>, len=%d, a=%d \n", buf_return, len, (int)buf_return[0]);
935 #endif
936 				    }
937 
938 				}
939 #endif
940 			    }
941 			}
942 			break;
943 		    }
944 
945 		default: /* event we don't care about or garbage! */
946 		    break;
947 
948 		} /* end event type switch */
949 	} /* end if have an event */
950     } /* end while (stat) */
951 
952     /* foil compiler warning */
953     unused = NULL;
954 }
955 #endif /* RM_X */
956 
957 /*
958  * ----------------------------------------------------
959  * @Name rmauxSetGeomTransform
960  @pstart
961  void rmauxSetGeomTransform (RMnode *target,
962 	                     RMpipe *usePipe)
963  @pend
964 
965  @astart
966  RMnode *target - a handle to an RMnode. (input) All model
967     transformations will be applied to this node, and will affect all
968     children nodes.
969 
970  RMpipe *usePipe - a handle to an RMpipe. (input) This is is the
971     currently active GL context and is used to get cursors for X11.
972  @aend
973 
974  @dstart
975 
976  rmauxSetGeomTransform assigns a default set of action handlers that will
977  be executed when rmauxEventLoop is invoked to begin processing of
978  events. The action handlers installed by rmauxSetGeomTransform will
979  modify the transformation matrices at the RMnode "subTree" to produce
980  either rotations (button2 + motion) or isometric scaling
981  (shift+button2+motion).
982 
983  NOTE: (8/27/2000) rmauxSetGeomTransform is identical to rmauxUI().
984  A new routine, rmauxSetCamera3DTransform, has been added to OpenRM
985  and may be used to manipulate 3d camera parameters to achieve
986  image plane translation, as well as camera dollying along the
987  Z axis in eye-coordinates.
988 
989  rmauxSetGeomTransform maps RM_BUTTON2 to an arcball-style rotation, and
990  SHIFT+RM_BUTTON2 to isometric scaling.  Pressing the RM_BUTTON* on the
991  mouse will initiate the transformation. Dragging the mouse while holding
992  the RM_BUTTON* down will cause a frame to be rendered for each
993  transformation (see Note below), then when the RM_BUTTON* is released,
994  the appearance of interactive transformation stops and the system
995  becomes quiescient.
996 
997  Pressing any key on the keyboard other than a modifier key will cause
998  the event loop to latch the pressed key.  Once the key is released, a
999  non-NULL user callback function for handling keypresses is called.
1000  This user key callback is set with rmauxEventLoop(). In the
1001  event no keypress callback is assigned with rmauxEventLoop, the event
1002  loop will exit if any non-modifier key is pressed (X11 only, Win32
1003  code incomplete at this time 8/27/2000).
1004 
1005  Applications developers may assign their own callbacks to the rmaux
1006  action handler using rmauxButtonDownFunc, rmauxButtonMotionFunc and
1007  rmauxButtonUpFunc (as well as rmauxSetIdleFunc). rmauxUI just assigns
1008  a default set of action handlers to a small, specific set of button
1009  events.
1010 
1011  Note: none of the rmaux routines are needed to use the rest of
1012  OpenRM. Your application may use it's own event loop, and you may
1013  safely ignore all of rmaux if that's what you need.
1014 
1015  Assumptions:
1016 
1017  1. Whenever a frame needs to be drawn, the routine specified by
1018  rmauxSetRenderFunc is invoked. By default, that function is just
1019  rmFrame(). rmFrame() draws everything starting at the scene graph
1020  node rmRootNode().
1021 
1022  2. X11 notes: rmauxUI creates cursors for each operation. These
1023  cursors are valid only on the XDisplay of the currently active
1024  RMpipe. Be sure to call rmPipeMakeCurrent prior to calling rmauxUI.
1025  At this time (1/15/2000) this restriction does not apply to Win32
1026  codes.
1027 
1028  @dend
1029  * ----------------------------------------------------
1030  */
1031 void
rmauxSetGeomTransform(RMnode * target,RMpipe * usePipe)1032 rmauxSetGeomTransform(RMnode *target,
1033 		      RMpipe *usePipe)
1034 {
1035 #ifdef RM_X
1036     XColor cursor_fg, cursor_bg;
1037 
1038     /* define special X11 cursors for operation emphasis */
1039     rotate_cursor = XCreateFontCursor(rmxPipeGetDisplay(usePipe), XC_exchange);
1040     scale_cursor = XCreateFontCursor(rmxPipeGetDisplay(usePipe), XC_sizing);
1041 
1042     /* define cursor colors
1043      * (r,g,b) = [0..65535] in X11 colors
1044      * (the XServer scales X11 colors to available HW color bit depth)
1045      */
1046     cursor_fg.red = 65535;
1047     cursor_fg.green = 0;
1048     cursor_fg.blue = 16383;
1049     cursor_bg.red = 65535;
1050     cursor_bg.green = 65535;
1051     cursor_bg.blue = 65535;
1052 
1053     /* color special cursors to emphasize during operation */
1054     XRecolorCursor(rmxPipeGetDisplay(usePipe), rotate_cursor, &cursor_fg, &cursor_bg);
1055     XRecolorCursor(rmxPipeGetDisplay(usePipe), scale_cursor, &cursor_fg, &cursor_bg);
1056 #endif
1057     geomTransformTarget = target;
1058 
1059     /* assign RM_BUTTON2: Arcball rotation */
1060     rmauxSetButtonDownFunc(RM_BUTTON2, RM_NONE_MODMASK, rmauxB2DownFunc);
1061     rmauxSetButtonUpFunc(RM_BUTTON2, RM_NONE_MODMASK, rmauxB2UpFunc);
1062     rmauxSetButtonMotionFunc(RM_BUTTON2, RM_NONE_MODMASK, rmauxB2MotionFunc);
1063 
1064     /* shift+button two = scale */
1065     rmauxSetButtonDownFunc(RM_BUTTON2,RM_SHIFT_MODMASK, rmauxShiftB2DownFunc);
1066     rmauxSetButtonUpFunc(RM_BUTTON2,RM_SHIFT_MODMASK, rmauxShiftB2UpFunc);
1067     rmauxSetButtonMotionFunc(RM_BUTTON2,RM_SHIFT_MODMASK, rmauxShiftB2MotionFunc);
1068 }
1069 
1070 /*
1071  * ----------------------------------------------------
1072  * @Name rmauxSetCamera3DTransform
1073  @pstart
1074  void rmauxSetCamera3DTransform (RMnode *target,
1075 	                         RMpipe *usePipe)
1076  @pend
1077 
1078  @astart
1079  RMnode *target - a handle to an RMnode. (input) All transformations
1080     will be applied to the RMcamera3D scene parameter at target, if
1081     such a scene parameter exists.
1082 
1083  RMpipe *usePipe - a handle to an RMpipe. (input) This is is the
1084     currently active GL context and is used to get cursors for X11.
1085  @aend
1086 
1087  @dstart
1088 
1089  rmauxSetCamera3DTransform assigns a default set of action handlers that will
1090  be executed when rmauxEventLoop is invoked to begin processing of
1091  events. The action handlers installed by rmauxSetCamera3DTransform will
1092  modify eye and look-at attributes of the RMcamera3D scene parameter
1093  assigned at the input RMnode "target" to achieve image plane
1094  translation, or dollying along the Z axis of eye coordinates.
1095 
1096  rmauxSetCamera3DTransform maps RM_BUTTON1 to image plane translation,
1097  and RM_BUTTON3 to camera dolly translation. Pressing the associated
1098  RM_BUTTON* initiates the transformation.  Dragging the mouse while holding
1099  the RM_BUTTON* down will cause a frame to be rendered for each
1100  transformation (see Note below), then when the RM_BUTTON* is released,
1101  the appearance of interactive transformation stops and the system
1102  becomes quiescient.
1103 
1104  Pressing any key on the keyboard other than a modifier key will cause
1105  the event loop to latch the pressed key.  Once the key is released, a
1106  non-NULL user callback function for handling keypresses is called.
1107  This user key callback is set with rmauxEventLoop(). In the
1108  event no keypress callback is assigned with rmauxEventLoop, the event
1109  loop will exit if any non-modifier key is pressed (X11 only, Win32
1110  code incomplete at this time 8/27/2000).
1111 
1112  Applications developers may assign their own callbacks to the rmaux
1113  action handler using rmauxButtonDownFunc, rmauxButtonMotionFunc and
1114  rmauxButtonUpFunc (as well as rmauxSetIdleFunc). rmauxUI just assigns
1115  a default set of action handlers to a small, specific set of button
1116  events.
1117 
1118  Note: none of the rmaux routines are needed to use the rest of
1119  OpenRM. Your application may use it's own event loop, and you may
1120  safely ignore all of rmaux if that's what you need.
1121 
1122  Assumptions:
1123 
1124  1. Whenever a frame needs to be drawn, the routine specified by
1125  rmauxSetRenderFunc is invoked. By default, that function is just
1126  rmFrame(). rmFrame() draws everything starting at the scene graph
1127  node rmRootNode().
1128 
1129  2. X11 notes: rmauxUI creates cursors for each operation. These
1130  cursors are valid only on the XDisplay of the currently active
1131  RMpipe. Be sure to call rmPipeMakeCurrent prior to calling rmauxUI.
1132  At this time (1/15/2000) this restriction does not apply to Win32
1133  codes.
1134 
1135  @dend
1136  * ----------------------------------------------------
1137  */
1138 void
rmauxSetCamera3DTransform(RMnode * target,RMpipe * usePipe)1139 rmauxSetCamera3DTransform(RMnode *target,
1140 			  RMpipe *usePipe)
1141 {
1142 #ifdef RM_X
1143     XColor cursor_fg, cursor_bg;
1144 
1145     /* define special X11 cursors for operation emphasis */
1146     translate_cursor = XCreateFontCursor(rmxPipeGetDisplay(usePipe), XC_fleur);
1147     dolly_cursor = XCreateFontCursor(rmxPipeGetDisplay(usePipe), XC_double_arrow);
1148     /* define cursor colors
1149      * (r,g,b) = [0..65535] in X11 colors
1150      * (the XServer scales X11 colors to available HW color bit depth)
1151      */
1152     cursor_fg.red = 65535;
1153     cursor_fg.green = 0;
1154     cursor_fg.blue = 16383;
1155     cursor_bg.red = 65535;
1156     cursor_bg.green = 65535;
1157     cursor_bg.blue = 65535;
1158 
1159     /* color special cursors to emphasize during operation */
1160     XRecolorCursor(rmxPipeGetDisplay(usePipe), translate_cursor, &cursor_fg, &cursor_bg);
1161     XRecolorCursor(rmxPipeGetDisplay(usePipe), dolly_cursor, &cursor_fg, &cursor_bg);
1162 #endif
1163     camera3DTransformTarget = target;
1164 
1165     /* assign RM_BUTTON1: image plane translation */
1166     rmauxSetButtonDownFunc(RM_BUTTON1, RM_NONE_MODMASK, rmauxB1DownFunc);
1167     rmauxSetButtonUpFunc(RM_BUTTON1, RM_NONE_MODMASK, rmauxB1UpFunc);
1168     rmauxSetButtonMotionFunc(RM_BUTTON1, RM_NONE_MODMASK, rmauxB1MotionFunc);
1169 
1170     /* assign RM_BUTTON3: camera dolly translation */
1171     rmauxSetButtonDownFunc(RM_BUTTON3, RM_NONE_MODMASK, rmauxB3DownFunc);
1172     rmauxSetButtonUpFunc(RM_BUTTON3, RM_NONE_MODMASK, rmauxB3UpFunc);
1173     rmauxSetButtonMotionFunc(RM_BUTTON3, RM_NONE_MODMASK, rmauxB3MotionFunc);
1174 }
1175 
1176 
1177 /*
1178  * ----------------------------------------------------
1179  * @Name rmauxUI
1180  @pstart
1181  void rmauxUI (RMnode *target,
1182 	       RMpipe *usePipe)
1183  @pend
1184 
1185  @astart
1186  RMnode *target - a handle to an RMnode. (input) All model
1187     transformations will be applied to this node, and will affect all
1188     children nodes.
1189 
1190  RMpipe *usePipe - a handle to an RMpipe. (input) This is is the
1191     currently active GL context and is used to get cursors for X11.
1192  @aend
1193 
1194  @dstart
1195 
1196  rmauxUI assigns a default set of action handlers that will be
1197  executed when rmauxEventLoop is invoked to begin processing of
1198  events. There are three types of transformations implemented by
1199  rmauxUI: a virtual arcball interface to specify rotations, a camera
1200  dolly translation, and an image plane translation.
1201 
1202  rmauxUI maps RM_BUTTON2 to an arcball-style rotation, and
1203  SHIFT+RM_BUTTON2 to isometric scaling.  Pressing the RM_BUTTON* on the
1204  mouse will initiate the transformation. Dragging the mouse while holding
1205  the RM_BUTTON* down will cause a frame to be rendered for each
1206  transformation (see Note below), then when the RM_BUTTON* is released,
1207  the appearance of interactive transformation stops and the system
1208  becomes quiescient.
1209 
1210  Pressing any key on the keyboard other than a modifier key will cause
1211  the event loop to latch the pressed key.  Once the key is released, a
1212  non-NULL user callback function for handling keypresses is called.
1213  This user key callback is set with rmauxEventLoop().
1214 
1215  Resizing the window bound to the RMpipe will call a non-NULL user
1216  callback function for handling window resizing.  This user resize
1217  callback is set with rmauxEventLoop().
1218 
1219  Applications developers may assign their own callbacks to the rmaux
1220  action handler using rmauxButtonDownFunc, rmauxButtonMotionFunc and
1221  rmauxButtonUpFunc (as well as rmauxSetIdleFunc). rmauxUI just assigns
1222  a default set of action handlers to a small, specific set of button
1223  events.
1224 
1225  Note: none of the rmaux routines are needed to use the rest of
1226  OpenRM. Your application may use it's own event loop, and you may
1227  safely ignore all of rmaux if that's what you need.
1228 
1229  Assumptions:
1230 
1231  1. Whenever a frame needs to be drawn, the routine specified by
1232  rmauxSetRenderFunc is invoked. By default, that function is just
1233  rmFrame(). rmFrame() draws everything starting at the scene graph
1234  node rmRootNode().
1235 
1236  2. X11 notes: rmauxUI creates cursors for each operation. These
1237  cursors are valid only on the XDisplay of the currently active
1238  RMpipe. Be sure to call rmPipeMakeCurrent prior to calling rmauxUI.
1239  At this time (1/15/2000) this restriction does not apply to Win32
1240  codes.
1241 
1242  @dend
1243  * ----------------------------------------------------
1244  */
1245 void
rmauxUI(RMnode * target,RMpipe * usePipe)1246 rmauxUI (RMnode *target,
1247 	 RMpipe *usePipe)
1248 {
1249 #ifdef RM_X
1250     XColor cursor_fg, cursor_bg;
1251 
1252     /* define special X11 cursors for operation emphasis */
1253     translate_cursor = XCreateFontCursor(rmxPipeGetDisplay(usePipe), XC_fleur);
1254     rotate_cursor = XCreateFontCursor(rmxPipeGetDisplay(usePipe), XC_exchange);
1255     dolly_cursor = XCreateFontCursor(rmxPipeGetDisplay(usePipe), XC_double_arrow);
1256     scale_cursor = XCreateFontCursor(rmxPipeGetDisplay(usePipe), XC_sizing);
1257 
1258     /* define cursor colors
1259      * (r,g,b) = [0..65535] in X11 colors
1260      * (the XServer scales X11 colors to available HW color bit depth)
1261      */
1262     cursor_fg.red = 65535;
1263     cursor_fg.green = 0;
1264     cursor_fg.blue = 16383;
1265     cursor_bg.red = 65535;
1266     cursor_bg.green = 65535;
1267     cursor_bg.blue = 65535;
1268 
1269     /* color special cursors to emphasize during operation */
1270     XRecolorCursor(rmxPipeGetDisplay(usePipe), rotate_cursor, &cursor_fg, &cursor_bg);
1271     XRecolorCursor(rmxPipeGetDisplay(usePipe), scale_cursor, &cursor_fg, &cursor_bg);
1272 #endif
1273 
1274     /* save `UI root' node as target for interaction */
1275     if (target == NULL)
1276     {
1277         rmError("rmauxUI() error - input node is NULL.");
1278 	return;
1279     }
1280     geomTransformTarget = target;
1281 
1282     /* assign RM_BUTTON2: Arcball rotation */
1283     rmauxSetButtonDownFunc(RM_BUTTON2, RM_NONE_MODMASK, rmauxB2DownFunc);
1284     rmauxSetButtonUpFunc(RM_BUTTON2, RM_NONE_MODMASK, rmauxB2UpFunc);
1285     rmauxSetButtonMotionFunc(RM_BUTTON2, RM_NONE_MODMASK, rmauxB2MotionFunc);
1286 
1287     /* shift+button two = scale */
1288     rmauxSetButtonDownFunc(RM_BUTTON2,RM_SHIFT_MODMASK, rmauxShiftB2DownFunc);
1289     rmauxSetButtonUpFunc(RM_BUTTON2,RM_SHIFT_MODMASK, rmauxShiftB2UpFunc);
1290     rmauxSetButtonMotionFunc(RM_BUTTON2,RM_SHIFT_MODMASK, rmauxShiftB2MotionFunc);
1291 
1292 }
1293 
1294 
1295 /*
1296  * ----------------------------------------------------
1297  * @Name rmauxSetInitFunc
1298  @pstart
1299  rmauxSetInitFunc (void (*userinitfunc)(RMpipe *drawOn, RMnode *subTree))
1300  @pend
1301 
1302  @astart
1303  void (*userinitfunc)(RMpipe *drawOnPipe, RMnode *subtree) - a handle to a caller-supplied
1304     initialization function.
1305  @aend
1306 
1307  @dstart
1308 
1309  Use this routine to register your application's "init" function with
1310  RMaux. This initialization function will be called from within
1311  rmauxEventLoop after all OpenGL and window system initialization had
1312  been complete, and it is safe to begin using all of OpenRM.
1313 
1314  @dend
1315  * ----------------------------------------------------
1316  */
1317 void
rmauxSetInitFunc(void (* userinitfunc)(RMpipe *,RMnode *))1318 rmauxSetInitFunc (void (*userinitfunc)(RMpipe *, RMnode *))
1319 {
1320     staticUserInitFunc = userinitfunc;
1321 }
1322 
1323 
1324 /*
1325  * ----------------------------------------------------
1326  * @Name rmauxSetIdleFunc
1327  @pstart
1328  void rmauxSetIdleFunc (RMpipe *p,
1329                         int (*userfunc)(RMpipe *currentPipe, int pointerX, int pointerY))
1330 
1331  @pend
1332 
1333  @astart
1334  RMpipe *p - a handle to an active RMpipe object.
1335 
1336  int (*userfunc)(RMpipe *currentPipe, int pointerX, int pointerY) - a
1337     handle to an application function that will be called when the
1338     system is idle. The routine will be passed the current (x,y) pixel
1339     location of the pointer, along with a handle to the controlling
1340     RMpipe. When the app idle callback returns a zero, event loop
1341     processing is terminated; when a non-zero value is returned,
1342     processing continues.
1343  @aend
1344 
1345  @dstart
1346 
1347   Use this routine to register an application function that will be
1348   called (X11) when there are no other events in the event queue or
1349   (Win32) a timer with a 20msec interval "alarm" has expired.
1350 
1351   Win32 Notes: we need the RMpipe pointer so that we can obtain the
1352   window handle for the purpose of attaching a "timer" function to a
1353   window.  because the timer goes off at a specified interval (in the
1354   absence of other messages to process), it's not really an "idle"
1355   function except in the sense that the app function is called 20msec
1356   after a period of idleness is detected.
1357 
1358   X: it really is an idle function. note that we detect idleness
1359   inside the event loop attached to a window, and don't need the
1360   RMpipe structure.  it is included here so that one API can be used
1361   for both Win32 and X.
1362 
1363   The value returned by the application idle callback has an effect
1364   upon event loop processing: when the app callback returns a zero,
1365   event loop processing is terminated. When a non-zero value is
1366   returned, event loop processing continues.
1367 
1368  @dend
1369  * ----------------------------------------------------
1370  */
1371 void
rmauxSetIdleFunc(RMpipe * p,int (* userfunc)(RMpipe * currentPipe,int pointerX,int pointerY))1372 rmauxSetIdleFunc (RMpipe *p,
1373 		  int (*userfunc)(RMpipe *currentPipe, int pointerX, int pointerY))
1374 {
1375    staticUserIdleFunc = userfunc;
1376 #ifdef RM_WIN
1377    {
1378        HWND hWnd;
1379 
1380        hWnd = rmPipeGetWindow(p);
1381 
1382        if (hWnd != 0)
1383        {
1384 	   if (userfunc != NULL)
1385 	     SetTimer(hWnd, 101, 1, NULL); /* call every 1 msec when no other msgs present */
1386 	   else if (spinCallbackFunc == NULL) /* don't turn off timer if
1387 						 we a spinCallbackFunc is set*/
1388 	     KillTimer(hWnd, 101);
1389        }
1390    }
1391 #endif
1392 
1393    /* foil compiler warning */
1394    p = NULL;
1395 }
1396 
1397 /*
1398  * ----------------------------------------------------
1399  * @Name rmauxSetKeyFunc
1400  @pstart
1401  void rmauxSetKeyFunc (int (*userfunc)(RMpipe *p, char k, KeySym code))
1402  @pend
1403 
1404  @astart
1405  int (*userfunc)(RMpipe *p, char k, KeySym code) - a pointer to a user function
1406     (input).
1407  @aend
1408 
1409  @dstart
1410 
1411  Use rmauxSetKeyFunc to assign a keypress handler that will be invoked by
1412  rmauxEventLoop whenever a non-modifier keypress is detected. The userFunc
1413  will be provided the keyboard key that was pressed, the current RMpipe, and a
1414  KeySym code. Unlike mouse event mappings, where a different callback is
1415  invoked depending upon motion, button up or down events, a single key handler
1416  is used to perform processing of all keyboard events. Application-supplied
1417  key handler callbacks must contain sufficient internal logic to detect
1418  which key was pressed, and take appropriate action.
1419 
1420  The userFunc should return RM_CHILL to rmauxEventLoop if event processing
1421  is to continue, or return RM_WHACKED if event loop processing should
1422  continue.
1423 
1424  The RM demo programs typically assign the default key handler
1425  rmauxDefaultKeyFunc, which will terminate event loop processing whenever
1426  the user presses the "q" key on the keyboard.
1427 
1428  Notes:
1429 
1430  4/20/02 - this routine was updated to unify keypress handling on both
1431  X11 and Win32 platforms. The same API is used in both places, but the
1432  KeySym type is artificially defined on Win32 in order to coerce API
1433  consistency across platforms. Presently, the KeySym "code" parameter
1434  is not used on Win32. Only printable ASCII chars will trigger a call to
1435  the user-defined key-handling function on Win32; refer to your Win32
1436  developer documentation to learn more about which keys actually trigger
1437  an WM_CHAR event, which in turn initiates a call to the user-supplied
1438  key-handling callback.
1439 
1440  @dend
1441  * ----------------------------------------------------
1442  */
1443 void
rmauxSetKeyFunc(RMpipe * p,int (* userfunc)(RMpipe * currentPipe,char k,KeySym code))1444 rmauxSetKeyFunc (RMpipe *p,
1445 		 int (*userfunc)(RMpipe *currentPipe, char k, KeySym code))
1446 {
1447     staticUserKeyFunc = userfunc;
1448 
1449     /* foil compiler warning */
1450     p = NULL;
1451 }
1452 
1453 
1454 /*
1455  * ----------------------------------------------------
1456  * @Name rmauxDefaultKeyFunc
1457  @pstart
1458  int rmauxDefaultKeyFunc (RMpipe *currentPipe,
1459                           char    key,
1460 			  KeySym  code)
1461  @pend
1462 
1463  @astart
1464  RMpipe *currentPipe - a handle to the current pipe (input)
1465  char key - key from most recent key press (input)
1466  KeySym code (input) - an X11 KeySym code.
1467  @aend
1468 
1469  @dstart
1470 
1471  This simple key callback function intercepts "Q/q" key presses and
1472  then signals to quit application.
1473 
1474  4/20/02 - this routine was updated to unify keypress handling on both
1475  X11 and Win32 platforms. The same API is used in both places, but the
1476  KeySym type is artificially defined on Win32 in order to coerce API
1477  consistency across platforms. Presently, the KeySym "code" parameter
1478  is not used on Win32. Only printable ASCII chars will trigger a call to
1479  the user-defined key-handling function on Win32; refer to your Win32
1480  developer documentation to learn more about which keys actually trigger
1481  an WM_CHAR event, which in turn initiates a call to the user-supplied
1482  key-handling callback.
1483 
1484  3/27/04 - the input parameter "code" is ignored by this routine. The
1485  parameter is present to facilitate API consistency between user-supplied
1486  key handler callbacks and this RMaux default key handler.
1487 
1488  @dend
1489  * ----------------------------------------------------
1490  */
1491 int
rmauxDefaultKeyFunc(RMpipe * currentPipe,char key,KeySym code)1492 rmauxDefaultKeyFunc (RMpipe *currentPipe,
1493 		     char    key,
1494 		     KeySym  code)
1495 {
1496     /* convert to lower case */
1497     if ((key >= 'A') && (key <= 'Z'))
1498 	key = key - ('A' - 'a');
1499 
1500     /* take appropriate menu action */
1501     switch (key)
1502     {
1503        case 'q': /* quitting time */
1504 	   return (RM_WHACKED);
1505 
1506        default: /* do nothing */
1507 	   break;
1508     }
1509 
1510     /* foil compiler warning */
1511     currentPipe = NULL;
1512     code = (KeySym)0;
1513 
1514     return (RM_CHILL);
1515 }
1516 
1517 
1518 /*
1519  * ----------------------------------------------------
1520  * @Name rmauxSetResizeFunc
1521  @pstart
1522  void rmauxSetResizeFunc (RMpipe *p,
1523                           RMnode *cameraNode,
1524 			  RMenum (*userfunc)(RMpipe *p, RMnode *n, int winWidth, int winHeight))
1525  @pend
1526 
1527  @astart
1528  RMpipe *p - an RMpipe object (input).
1529 
1530  RMnode *cameraNode - a handle to an RMnode, which should contain either
1531     an RMcamera2D or RMcamera3D scene parameter (input, but modified at
1532     runtime when a window resize event occurs).
1533 
1534  void (*userfunc)(RMpipe *p, RMnode *n, int winWidth, int winHeight) - a pointer to a user function (input).
1535  @aend
1536 
1537  @dstart
1538 
1539  The rmaux*UI()/rmauxEventLoop routines will catch all resize events to
1540  the application window.  The user may inmplement a routine to handle
1541  window resizing and specify it here.
1542 
1543  The default resize function routine used by rmauxSetResizeFunc()
1544  adjusts camera parameters according to the resized window geometry.
1545 
1546  See rmauxDefaultResizeFunc() for more details about the parameters
1547  to the resize callback function.
1548 
1549  @dend
1550  * ----------------------------------------------------
1551  */
1552 void
rmauxSetResizeFunc(RMpipe * p,RMnode * cameraNode,int (* userfunc)(RMpipe * currentPipe,RMnode * cameraNode,int pointerX,int pointerY))1553 rmauxSetResizeFunc (RMpipe *p,
1554 		    RMnode *cameraNode,
1555 		    int (*userfunc)(RMpipe *currentPipe, RMnode *cameraNode, int pointerX, int pointerY))
1556 {
1557     staticUserResizeFunc = userfunc;
1558     private_rmauxSetCameraResizeNode(cameraNode);
1559 
1560     /* foil compiler warning */
1561     p = NULL;
1562 }
1563 
1564 
1565 /*
1566  * ----------------------------------------------------
1567  * @Name rmauxDefaultResizeFunc
1568  @pstart
1569  int rmauxDefaultResizeFunc (RMpipe *currentPipe,
1570                              RMnode *cameraNode,
1571 			     int     winWidth,
1572 			     int     winHeight)
1573  @pend
1574 
1575  @astart
1576  RMpipe *currentPipe - a handle to the current pipe
1577 
1578  RMnode *cameraNode - a handle to a scene graph node containing either
1579     an RMcamera2D or RMcamera3D scene parameter.
1580 
1581  int winWidth, winHeight - two integers specifying the new window
1582     size in pixels. These values are provided by rmauxEventLoop to
1583     the resize callback (input).
1584  @aend
1585 
1586  @dstart
1587 
1588  This simple resize callback function will adjust the aspect ratio of
1589  the 2D or 3D camera scene parameter contained in "cameraNode" to
1590  reflect the ratio of width to height dimensions of the resized window.
1591 
1592  If the target node "cameraNode" does not contain either a RMcamera2D
1593  or RMcamera3D scene parameter, this routine will issue a warning message.
1594 
1595  This routine always returns RM_CHILL, regardless of errors.
1596 
1597  @dend
1598  * ----------------------------------------------------
1599  */
1600 int
rmauxDefaultResizeFunc(RMpipe * currentPipe,RMnode * cameraNode,int winWidth,int winHeight)1601 rmauxDefaultResizeFunc (RMpipe *currentPipe,
1602 			RMnode *cameraNode,
1603 			int     winWidth,
1604 			int     winHeight)
1605 {
1606     RMcamera3D * c3=NULL;
1607     RMcamera2D * c2=NULL;
1608     RMnode *     r=NULL;
1609 
1610     r = cameraNode;
1611     if ((rmNodeGetSceneCamera3D (r, &c3)) != RM_WHACKED)
1612     {
1613 	rmCamera3DSetAspectRatio (c3, ((float)(winWidth) / (float)(winHeight)));
1614 	rmNodeSetSceneCamera3D (r, c3);
1615     }
1616     else if (rmNodeGetSceneCamera2D (r, &c2) != RM_WHACKED)
1617     {
1618 	rmCamera2DSetAspectRatio (c2, ((float)(winWidth) / (float)(winHeight)));
1619 	rmNodeSetSceneCamera2D (r, c2);
1620     }
1621 
1622     if ((c2 == NULL) && (c3 == NULL))
1623 	rmWarning("rmauxDefaultResizeFunc() - the cameraNode does not contain either a 2D or 3D camera scene parameter!");
1624 
1625     if (c3 != NULL)
1626 	rmCamera3DDelete(c3);
1627 
1628     if (c2 != NULL)
1629 	rmCamera2DDelete(c2);
1630 
1631     rmPipeSetWindowSize(currentPipe, winWidth, winHeight);
1632 
1633     return (RM_CHILL);
1634 }
1635 
1636 /*
1637  * ----------------------------------------------------
1638  * @Name rmauxSetRenderFunc
1639  @pstart
1640  void rmauxSetRenderFunc (void (*userfunc)(RMpipe *currentPipe))
1641  @pend
1642 
1643  @astart
1644  void (*userfunc)(RMpipe *currentPipe) - a pointer to a user function
1645     (input).
1646  @aend
1647 
1648  @dstart
1649 
1650  The rmaux*UI()/rmauxEventLoop routines will occasionally have the
1651  need to render an image. For the trackball interface, rendering is
1652  needed whenever the mouse button is pressed and dragged. The
1653  rmauxEventLoop event handler allows applications to set a routine
1654  that is called whenever rendering is needed.
1655 
1656  The default rendering routine used by rmauxSetRenderFunc() calls the
1657  generic rendering routine rmFrame().
1658 
1659  @dend
1660  * ----------------------------------------------------
1661  */
1662 void
rmauxSetRenderFunc(void (* userfunc)(RMpipe * p,RMnode * n))1663 rmauxSetRenderFunc (void (*userfunc)(RMpipe *p, RMnode *n))
1664 {
1665     if (userfunc != NULL)
1666 	renderfunc = userfunc;
1667 }
1668 
1669 
1670 /*
1671  * ----------------------------------------------------
1672  * @Name rmauxSetButtonDownFunc
1673  @pstart
1674  void rmauxSetButtonDownFunc (unsigned int whichbutton,
1675 		              unsigned int modmask,
1676 		              int (*userfunc) (RMpipe *currentPipe, int pointerX, int pointerY))
1677  @pend
1678 
1679  @astart
1680  unsigned int whichbutton - An integer value specifying which button
1681     will be assigned an action handler. Valid values are RM_BUTTON1,
1682     RM_BUTTON2, RM_BUTTON3, RM_BUTTON4 or RM_BUTTON5. The enumerators
1683     RM_BUTTON4 and RM_BUTTON5 are valid in X11 environments, but not
1684     valid in Windows environments.
1685 
1686  unsigned int modmask - An integer value specifying a modifier mask
1687     that can be used to qualify buttons. Valid values are
1688     RM_NONE_MODMASK, RM_SHIFT_MODMASK or RM_CONTROL_MODMASK.
1689 
1690  int (*userfunc) (RMpipe *currentPipe, int xPointerPosition, int
1691     yPointerPosition) - the function that will be invoked when the
1692     named button is pressed and modifier mask conditions are met. The
1693     user function will be passed integer values representing the (x,y)
1694     pixel coordinate of the pointer, along with a handle to the
1695     current/calling RMpipe.
1696  @aend
1697 
1698  @dstart
1699 
1700  Applications may use this routine to define action handlers that will
1701  be invoked from inside of rmauxEventLoop(). This routine is one of
1702  three used to assign an application callback that will be invoked
1703  when a button is pressed. One of two modifier keys may also be
1704  specified, the shift key or control key, to be used in conjunction
1705  with a button.
1706 
1707  This routine, rmauxSetButtonDownFunc, is used to assign a callback to
1708  correspond to button down events.
1709 
1710  The return value from the application callback is significant. A
1711  return value of zero will cause the event loop to terminate. A value
1712  of 1 cause the event loop to continue processing events (strictly
1713  speaking, any non-zero value will keep the event loop going).
1714 
1715  @dend
1716  * ----------------------------------------------------
1717  */
1718 void
rmauxSetButtonDownFunc(unsigned int whichbutton,unsigned int modmask,int (* userfunc)RMAUX_BUTTON_FUNC_PARMS ())1719 rmauxSetButtonDownFunc (unsigned int whichbutton,
1720 		        unsigned int modmask,
1721 		        int (*userfunc) RMAUX_BUTTON_FUNC_PARMS() )
1722 {
1723     int button_index, modifier_index;
1724 
1725     button_index = private_rmauxButtonNumToIndex(whichbutton);
1726     modifier_index = private_rmauxModifierToIndex(modmask);
1727 
1728     rmauxUserButtonDownFuncs[modifier_index][button_index] = userfunc;
1729 }
1730 
1731 
1732 /*
1733  * ----------------------------------------------------
1734  * @Name rmauxSetButtonUpFunc
1735  @pstart
1736  void rmauxSetButtonUpFunc (unsigned int whichbutton,
1737 		            unsigned int modmask,
1738 		            int (*userfunc) (RMpipe *currentPipe, int pointerX, int pointerY))
1739  @pend
1740 
1741  @astart
1742  unsigned int whichbutton - An integer value specifying which button
1743     will be assigned an action handler. Valid values are RM_BUTTON1,
1744     RM_BUTTON2, RM_BUTTON3, RM_BUTTON4 or RM_BUTTON5. The enumerators
1745     RM_BUTTON4 and RM_BUTTON5 are valid in X11 environments but not
1746     Windows environments.
1747 
1748  unsigned int modmask - An integer value specifying a modifier mask
1749     that can be used to qualify buttons. Valid values are
1750     RM_NONE_MODMASK, RM_SHIFT_MODMASK or RM_CONTROL_MODMASK.
1751 
1752  int (*userfunc) (RMpipe *currentPipe, int xPointerPosition, int
1753     yPointerPosition) - the function that will be invoked when the
1754     named button is released, and modifier mask conditions are
1755     met. The user function will be passed integer values representing
1756     the (x,y) pixel coordinate of the pointer, along with a handle to
1757     the current/calling RMpipe.
1758  @aend
1759 
1760  @dstart
1761 
1762  Applications may use this routine to define action handlers that will
1763  be invoked from inside of rmauxEventLoop(). This routine is one of
1764  three used to assign an application callback that will be invoked
1765  when a button release event is detected, and while modifier
1766  conditions are met. One of two modifier keys may also be specified,
1767  the shift key or control key, to be used in conjunction with a
1768  button.
1769 
1770  This routine, rmauxSetButtonUpFunc, is used to assign a callback to
1771  correspond to button button release events, possibly with a modifier
1772  key present.
1773 
1774  The return value from the application callback is significant. A
1775  return value of zero will cause the event loop to terminate. A value
1776  of 1 cause the event loop to continue processing events (strictly
1777  speaking, any non-zero value will keep the event loop going).
1778 
1779  @dend
1780  * ----------------------------------------------------
1781  */
1782 void
rmauxSetButtonUpFunc(unsigned int whichbutton,unsigned int modmask,int (* userfunc)RMAUX_BUTTON_FUNC_PARMS ())1783 rmauxSetButtonUpFunc (unsigned int whichbutton,
1784 		      unsigned int modmask,
1785 		      int (*userfunc) RMAUX_BUTTON_FUNC_PARMS() )
1786 {
1787     int button_index, modifier_index;
1788 
1789     button_index = private_rmauxButtonNumToIndex(whichbutton);
1790     modifier_index = private_rmauxModifierToIndex(modmask);
1791 
1792     rmauxUserButtonUpFuncs[modifier_index][button_index] = userfunc;
1793 }
1794 
1795 
1796 /*
1797  * ----------------------------------------------------
1798  * @Name rmauxSetButtonMotionFunc
1799  @pstart
1800  void rmauxSetButtonMotionFunc (unsigned int whichbutton,
1801 		                unsigned int modmask,
1802 		                int (*userfunc) (RMpipe *currentPipe, int pointerX, int pointerY))
1803  @pend
1804 
1805  @astart
1806  unsigned int whichbutton - An integer value specifying which button
1807     will be assigned an action handler. Valid values are RM_BUTTON1,
1808     RM_BUTTON2, RM_BUTTON3, RM_BUTTON4 or RM_BUTTON5. The enumerators
1809     RM_BUTTON4 and RM_BUTTON5 are valid in X11 environments but not
1810     Windows environments.
1811 
1812  unsigned int modmask - An integer value specifying a modifier mask
1813     that can be used to qualify buttons. Valid values are
1814     RM_NONE_MODMASK, RM_SHIFT_MODMASK or RM_CONTROL_MODMASK.
1815 
1816  int (*userfunc) (RMpipe *currentPipe, int xPointerPosition, int
1817     yPointerPosition) - the function that will be invoked when the
1818     named button is depressed, the the pointer is moving, and modifier
1819     mask conditions are met. The user function will be passed integer
1820     values representing the (x,y) pixel coordinate of the pointer,
1821     along with a handle to the current/calling RMpipe.
1822  @aend
1823 
1824  @dstart
1825 
1826  Applications may use this routine to define action handlers that will
1827  be invoked from inside of rmauxEventLoop(). This routine is one of
1828  three used to assign an application callback that will be invoked
1829  when a motion event is detected, while a button is pressed and while
1830  modifier conditions are met. One of two modifier keys may also be
1831  specified, the shift key or control key, to be used in conjunction
1832  with a button.
1833 
1834  This routine, rmauxSetButtonMotionFunc, is used to assign a callback
1835  to correspond to motion events while a button, and possibly a modifer
1836  key are depressed.
1837 
1838  The return value from the application callback is significant. A
1839  return value of zero will cause the event loop to terminate. A value
1840  of 1 cause the event loop to continue processing events (strictly
1841  speaking, any non-zero value will keep the event loop going).
1842 
1843  @dend
1844  * ----------------------------------------------------
1845  */
1846 void
rmauxSetButtonMotionFunc(unsigned int whichbutton,unsigned int modmask,int (* userfunc)RMAUX_BUTTON_FUNC_PARMS ())1847 rmauxSetButtonMotionFunc (unsigned int whichbutton,
1848 			  unsigned int modmask,
1849 			  int (*userfunc) RMAUX_BUTTON_FUNC_PARMS() )
1850 {
1851     int button_index, modifier_index;
1852 
1853     button_index = private_rmauxButtonNumToIndex(whichbutton);
1854     modifier_index = private_rmauxModifierToIndex(modmask);
1855 
1856     rmauxUserButtonMotionFuncs[modifier_index][button_index] = userfunc;
1857 }
1858 
1859 
1860 /* button callback default definition */
1861 static int
RMAUX_BUTTON_FUNC_PARMS()1862 rmauxEventNoOp RMAUX_BUTTON_FUNC_PARMS()
1863 {
1864     /* foil compiler warning */
1865     xbutton = ybutton = 0;
1866 
1867     /* foil compiler warning */
1868     p = NULL;
1869 
1870     return(1);
1871 }
1872 
1873 
1874 /* default button down functions [rmauxEventNoOp], modified by rmauxSetButtonDownFunc() */
1875 static int (*rmauxUserButtonDownFuncs[RM_NUM_BUTTON_MODIFIERS][RM_NUM_BUTTONS + 1]) RMAUX_BUTTON_FUNC_PARMS() =
1876 {
1877     {rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp},
1878     {rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp},
1879     {rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp}
1880 };
1881 
1882 
1883 /* default button up functions [rmauxEventNoOp], modified by rmauxSetButtonUpFunc() */
1884 static int (*rmauxUserButtonUpFuncs[RM_NUM_BUTTON_MODIFIERS][RM_NUM_BUTTONS + 1]) RMAUX_BUTTON_FUNC_PARMS() =
1885 {
1886     {rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp},
1887     {rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp},
1888     {rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp}
1889 };
1890 
1891 
1892 /* default button motion functions [rmauxEventNoOp], modified by rmauxSetButtonMotionFunc() */
1893 static int (*rmauxUserButtonMotionFuncs[RM_NUM_BUTTON_MODIFIERS][RM_NUM_BUTTONS + 1]) RMAUX_BUTTON_FUNC_PARMS() =
1894 {
1895     {rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp},
1896     {rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp},
1897     {rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp, rmauxEventNoOp}
1898 };
1899 
1900 
1901 /* PRIVATE (rmauxUI, rmFlyUI)
1902  *
1903  * intended for internal use, currently used only by rmFlyUI to
1904  * invoke the renderer. this routine will invoke the currently-registered
1905  * render function assigned to rmaux.
1906  *
1907  * since this is intended for internal use, it is not publicly documented.
1908  */
1909 void
rmauxInvokeRenderFunc(RMpipe * pipe,RMnode * subTreeToDraw)1910 rmauxInvokeRenderFunc (RMpipe *pipe,
1911 		       RMnode *subTreeToDraw)
1912 {
1913     (*renderfunc)(pipe, subTreeToDraw);
1914 }
1915 
1916 
1917 /* PRIVATE (rmauxUI)
1918  *
1919  * rmauxB1DownFunc is called from rmauxEventLoop when a
1920  * button1 down event is detected. rmauxUI assigns rmauxB1DownFunc
1921  * to be the action handler for button1 down.
1922  *
1923  * RM_BUTTON1: image plane translation
1924  *
1925  * applications may override this by calling rmauxSetButtonMotionFunc().
1926  * since this routine is intended to be called only from within
1927  * rmauxEventLoop, it is not publicly documented.
1928  */
1929 int
RMAUX_BUTTON_FUNC_PARMS()1930 rmauxB1DownFunc RMAUX_BUTTON_FUNC_PARMS()
1931 {
1932     int width, height;
1933 
1934 #ifdef RM_X
1935     XDefineCursor(rmxPipeGetDisplay(p), rmPipeGetWindow(p), translate_cursor);
1936 #endif
1937     /* save the starting NDC cursor position */
1938     rmPipeGetWindowSize(p, &width, &height);
1939     x = pixeltovp(xbutton, width);
1940     y = -1.0F * pixeltovp(ybutton, height);
1941 
1942     return(1);
1943 }
1944 
1945 
1946 /* PRIVATE (rmauxUI)
1947  *
1948  * rmauxB1UpFunc is called from rmauxEventLoop when a button2 up
1949  * event is detected.  rmauxUI assigns rmauxB1UpFunc
1950  * to be the action handler for button1 up.
1951  *
1952  * RM_BUTTON1: image plane translation
1953  *
1954  * applications may override this by calling rmauxSetButtonMotionFunc().
1955  * since this routine is intended to be called only from within
1956  * rmauxEventLoop, it is not publicly documented.
1957  */
1958 int
RMAUX_BUTTON_FUNC_PARMS()1959 rmauxB1UpFunc RMAUX_BUTTON_FUNC_PARMS()
1960 {
1961 #ifdef RM_X
1962     XUndefineCursor(rmxPipeGetDisplay(p), rmPipeGetWindow(p));
1963 #endif
1964 
1965     /* foil compiler warning */
1966     xbutton = ybutton = 0;
1967 
1968     return(1);
1969 }
1970 
1971 
1972 /* PRIVATE (rmauxUI)
1973  *
1974  * rmauxB1MotionFunc is called from rmauxEventLoop when a motion
1975  * event is detected while button1 is held down. rmauxUI assigns
1976  * rmauxB1MotionFunc to be the action handler for button1+motion.
1977  *
1978  * RM_BUTTON1: image plane translation
1979  *
1980  * applications may override this by calling rmauxSetButtonMotionFunc().
1981  * since this routine is intended to be called only from within
1982  * rmauxEventLoop, it is not publicly documented.
1983  */
1984 int
RMAUX_BUTTON_FUNC_PARMS()1985 rmauxB1MotionFunc RMAUX_BUTTON_FUNC_PARMS()
1986 {
1987     int   width, height;
1988     float x2, y2;
1989     RMcamera3D *c=NULL;
1990 
1991 
1992     /* get current NDC cursor position */
1993     rmPipeGetWindowSize(p, &width, &height);
1994     x2 = pixeltovp(xbutton, width);
1995     y2 = -1.0F * pixeltovp(ybutton, height);
1996 
1997     /* do specified image plane translation */
1998     if (rmNodeGetSceneCamera3D(camera3DTransformTarget, &c) != RM_WHACKED)
1999     {
2000 	rmauxTranslate(c, &x, &y, &x2, &y2);
2001 	rmNodeSetSceneCamera3D(camera3DTransformTarget, c);
2002 	rmCamera3DDelete(c);
2003     }
2004     x = x2;
2005     y = y2;
2006 
2007     /* redraw the frame */
2008     rmauxInvokeRenderFunc(p, private_rmauxGetCurrentSceneGraph());
2009 
2010     return(1);
2011 }
2012 
2013 
2014 /* PRIVATE (rmauxUI)
2015  *
2016  * rmauxB2DownFunc is called from rmauxEventLoop when a
2017  * button2 down event is detected. rmauxUI assigns rmauxB2DownFunc
2018  * to be the action handler for button2 down.
2019  *
2020  * RM_BUTTON2: Arcball rotation
2021  *
2022  * applications may override this by calling rmauxSetButtonMotionFunc().
2023  * since this routine is intended to be called only from within
2024  * rmauxEventLoop, it is not publicly documented.
2025  */
2026 int
RMAUX_BUTTON_FUNC_PARMS()2027 rmauxB2DownFunc RMAUX_BUTTON_FUNC_PARMS()
2028 {
2029     int width, height;
2030 
2031 #ifdef RM_X
2032     XDefineCursor(rmxPipeGetDisplay(p), rmPipeGetWindow(p), rotate_cursor);
2033 #endif
2034 
2035     if (spinCallbackFunc != NULL)
2036     {
2037 #ifdef RM_WIN
2038 	if (staticUserIdleFunc == NULL)
2039 	{
2040 	    /*
2041 	     * turn off the timed event generator if the user defined
2042 	     * idle callback isn't defined.
2043 	     */
2044 	    HWND hWnd;
2045 	    hWnd = rmPipeGetWindow(p);
2046 	    KillTimer(hWnd, 101);
2047 	}
2048 #endif
2049 	spinCallbackFunc = NULL;
2050     }
2051 
2052     /* save the starting NDC cursor position */
2053     rmPipeGetWindowSize(p, &width, &height);
2054 
2055     lastBX1 = lastBX2 = (float)xbutton;
2056     x = pixeltovp(xbutton, width);
2057 
2058     lastBY1 = lastBY2 = (float)ybutton;
2059     y = -1.0F * pixeltovp(ybutton, height);
2060 
2061     /* save the initial rotation matrix */
2062     if (rmNodeGetRotateMatrix(geomTransformTarget, &ui_pose) == RM_WHACKED)
2063 	rmMatrixIdentity(&ui_pose);
2064 
2065     return(1);
2066 }
2067 
2068 void
private_rmauxComputeScaledSpinPoints(float * px1,float * py1,float * px2,float * py2,int width,int height)2069 private_rmauxComputeScaledSpinPoints(float *px1,
2070 				     float *py1,
2071 				     float *px2,
2072 				     float *py2,
2073 				     int width,
2074 				     int height)
2075 {
2076     RMvertex2D p1, p2, v;
2077     double d;
2078 
2079     p1.x = pixeltovp(*px1, width);
2080     p1.y = pixeltovp(*py1, height);
2081 
2082     p2.x = pixeltovp(*px2, width);
2083     p2.y = pixeltovp(*py2, height);
2084 
2085     v.x = p2.x - p1.x;
2086     v.y = p2.y - p1.y;
2087 
2088     d = (v.x * v.x) + (v.y * v.y);
2089     d = sqrt(d);
2090 
2091     v.x *= d;
2092     v.y *= d;
2093 
2094     *px2 = *px1 + (v.x*width);
2095     *py2 = *py1 + (v.y*height);
2096 }
2097 
2098 /* PRIVATE (rmauxUI)
2099  *
2100  * rmauxB2UpFunc is called from rmauxEventLoop when a button2 up
2101  * event is detected.  rmauxUI assigns rmauxB2UpFunc
2102  * to be the action handler for button2 up.
2103  *
2104  * RM_BUTTON2: Arcball rotation
2105  *
2106  * applications may override this by calling rmauxSetButtonMotionFunc().
2107  * since this routine is intended to be called only from within
2108  * rmauxEventLoop, it is not publicly documented.
2109  */
2110 int
RMAUX_BUTTON_FUNC_PARMS()2111 rmauxB2UpFunc RMAUX_BUTTON_FUNC_PARMS()
2112 {
2113     int width, height;
2114 #ifdef RM_X
2115     XUndefineCursor(rmxPipeGetDisplay(p), rmPipeGetWindow(p));
2116 #endif
2117 
2118 /*    printf(" x,y at release %d, %d \n", xbutton, ybutton);  */
2119 
2120     if (spinModeEnabled == RM_TRUE)
2121     {
2122 	rmPipeGetWindowSize(p, &width, &height);
2123 
2124 #ifdef RM_WIN
2125 	if (private_rmauxSpinThreshold(lastBX2, lastBY2, lastBX1, lastBY1, staticSpinThreshold) > 0)
2126 #else
2127 	if (private_rmauxSpinThreshold(lastBX2, lastBY2, lastBX1, lastBY1, staticSpinThreshold) > 0)
2128 #endif
2129 	{
2130 	    spinCallbackFunc = private_rmauxDoSpinCallback;
2131 #ifdef RM_WIN
2132 	    {
2133 		HWND hWnd;
2134 		hWnd = rmPipeGetWindow(p);
2135 		/*
2136 		 * since we've exceeded the spin threshold, under Win32 we
2137 		 * need to activate a timer to generate events.
2138 		 */
2139 
2140 		if (hWnd != 0)
2141 		    SetTimer(hWnd, 101, 1, NULL); /* call every 20 msec when no other msgs present */
2142 
2143 	    }
2144 #endif
2145 	    {
2146 		float px1, py1, px2, py2;
2147 		px1 = lastBX2;
2148 		py1 = lastBY2;
2149 		px2 = lastBX1;
2150 		py2 = lastBY1;
2151 		private_rmauxComputeScaledSpinPoints(&px1, &py1, &px2, &py2, width, height);
2152 
2153 		px1 = pixeltovp(px1, width);
2154 		py1 = -1.0F * pixeltovp(py1, height);
2155 		px2 = pixeltovp(px2, width);
2156 		py2 = -1.0F * pixeltovp(py2, height);
2157 
2158 		rmauxArcBall(&px1, &py1, &px2, &py2, &spinMatrix);
2159 	    }
2160 	}
2161     }
2162 
2163     /* foil compiler warning */
2164     xbutton = ybutton = 0;
2165 
2166     return(1);
2167 }
2168 
2169 
2170 /* PRIVATE (rmauxUI)
2171  *
2172  * rmauxB2MotionFunc is called from rmauxEventLoop when a motion
2173  * event is detected while button2 is held down. rmauxUI assigns
2174  * rmauxB2MotionFunc to be the action handler for button2+motion.
2175  *
2176  * RM_BUTTON2: Arcball rotation
2177  *
2178  * applications may override this by calling rmauxSetButtonMotionFunc().
2179  * since this routine is intended to be called only from within
2180  * rmauxEventLoop, it is not publicly documented.
2181  */
2182 int
RMAUX_BUTTON_FUNC_PARMS()2183 rmauxB2MotionFunc RMAUX_BUTTON_FUNC_PARMS()
2184 {
2185     int   width, height;
2186     float x2, y2;
2187     RMmatrix resultMatrix;
2188 
2189 /*    printf(" x,y at motion %d, %d \n", xbutton, ybutton);  */
2190 
2191     /* get current NDC cursor position */
2192     rmPipeGetWindowSize(p, &width, &height);
2193 
2194     lastBX2 = lastBX1;
2195     lastBY2 = lastBY1;
2196 
2197     lastBY1 = (float)ybutton;
2198     y2 = -1.0F * pixeltovp(ybutton, height);
2199 
2200     lastBX1 = (float)xbutton;
2201     x2 = pixeltovp(xbutton, width);
2202 
2203     /* do specified Arcball rotation */
2204     rmauxArcBall(&x, &y, &x2, &y2, &resultMatrix);
2205     rmMatrixMultiply(&ui_pose, &resultMatrix, &resultMatrix);
2206     rmNodeSetRotateMatrix(geomTransformTarget, &resultMatrix);
2207 
2208     /* redraw the frame */
2209     rmauxInvokeRenderFunc(p, private_rmauxGetCurrentSceneGraph());
2210 
2211     return(1);
2212 }
2213 
2214 
2215 /* PRIVATE (rmauxUI)
2216  *
2217  * rmauxB3DownFunc is called from rmauxEventLoop when a
2218  * button3 down event is detected. rmauxUI assigns rmauxB3DownFunc
2219  * to be the action handler for button3 down.
2220  *
2221  * RM_BUTTON3: camera dolly translation
2222  *
2223  * applications may override this by calling rmauxSetButtonMotionFunc().
2224  * since this routine is intended to be called only from within
2225  * rmauxEventLoop, it is not publicly documented.
2226  */
2227 int
RMAUX_BUTTON_FUNC_PARMS()2228 rmauxB3DownFunc RMAUX_BUTTON_FUNC_PARMS()
2229 {
2230     int width, height;
2231 
2232 #ifdef RM_X
2233     XDefineCursor(rmxPipeGetDisplay(p), rmPipeGetWindow(p), dolly_cursor);
2234 #endif
2235     /* save the starting NDC cursor position */
2236     rmPipeGetWindowSize(p, &width, &height);
2237     x = pixeltovp(xbutton, width);
2238     y = -1.0F * pixeltovp(ybutton, height);
2239 
2240     return(1);
2241 }
2242 
2243 
2244 /* PRIVATE (rmauxUI)
2245  *
2246  * rmauxB3UpFunc is called from rmauxEventLoop when a button3 up
2247  * event is detected.  rmauxUI assigns rmauxB1UpFunc
2248  * to be the action handler for button3 up.
2249  *
2250  * RM_BUTTON3: camera dolly translation
2251  *
2252  * applications may override this by calling rmauxSetButtonMotionFunc().
2253  * since this routine is intended to be called only from within
2254  * rmauxEventLoop, it is not publicly documented.
2255  */
2256 int
RMAUX_BUTTON_FUNC_PARMS()2257 rmauxB3UpFunc RMAUX_BUTTON_FUNC_PARMS()
2258 {
2259 #ifdef RM_X
2260     XUndefineCursor(rmxPipeGetDisplay(p), rmPipeGetWindow(p));
2261 #endif
2262 
2263     /* foil compiler warning */
2264     xbutton = ybutton = 0;
2265 
2266     return(1);
2267 }
2268 
2269 
2270 /* PRIVATE (rmauxUI)
2271  *
2272  * rmauxB3MotionFunc is called from rmauxEventLoop when a motion
2273  * event is detected while button3 is held down. rmauxUI assigns
2274  * rmauxB3MotionFunc to be the action handler for button3+motion.
2275  *
2276  * RM_BUTTON3: camera dolly translation
2277  *
2278  * applications may override this by calling rmauxSetButtonMotionFunc().
2279  * since this routine is intended to be called only from within
2280  * rmauxEventLoop, it is not publicly documented.
2281  */
2282 int
RMAUX_BUTTON_FUNC_PARMS()2283 rmauxB3MotionFunc RMAUX_BUTTON_FUNC_PARMS()
2284 {
2285     int   width, height;
2286     float x2, y2;
2287     RMcamera3D *c=NULL;
2288 
2289     /* get current NDC cursor position */
2290     rmPipeGetWindowSize(p, &width, &height);
2291     x2 = pixeltovp(xbutton, width);
2292     y2 = -1.0F * pixeltovp(ybutton, height);
2293 
2294     /* do specified dolly translation */
2295     if (rmNodeGetSceneCamera3D(camera3DTransformTarget,&c) != RM_WHACKED)
2296     {
2297 	rmauxDolly(c, &x, &y, &x2, &y2);
2298 	rmNodeSetSceneCamera3D(camera3DTransformTarget,c);
2299 	rmCamera3DDelete(c);
2300     }
2301     x = x2;
2302     y = y2;
2303 
2304     /* redraw the frame */
2305     rmauxInvokeRenderFunc(p, private_rmauxGetCurrentSceneGraph());
2306 
2307     return(1);
2308 }
2309 
2310 int
RMAUX_BUTTON_FUNC_PARMS()2311 rmauxShiftB2DownFunc RMAUX_BUTTON_FUNC_PARMS()
2312 {
2313     int w,h;
2314 #ifdef RM_X
2315     XDefineCursor(rmxPipeGetDisplay(p),
2316 		  rmPipeGetWindow(p),scale_cursor);
2317 #endif
2318 
2319     rmPipeGetWindowSize(p,&w,&h);
2320     xscale_delta = 1.0F/(float)(w<<1);
2321     yscale_delta = 1.0F/(float)(h<<1);
2322     x = (float)xbutton;
2323     y = (float)ybutton;
2324     if (rmNodeGetScaleMatrix(geomTransformTarget,&saveScaleMatrix) == RM_WHACKED)
2325 	rmMatrixIdentity(&saveScaleMatrix);
2326 
2327     (*renderfunc)(p, private_rmauxGetCurrentSceneGraph());
2328 
2329     return(1);
2330 }
2331 
2332 /*
2333  * rmauxShiftB2MotionFunc is called from rmauxEventLoop when a motion
2334  * event is detected while button2 and the shift key are depressed.
2335  * rmauxUI assigns rmauxB2UpFunc
2336  * to be the action handler for shift+button2+motion. applications may
2337  * override this by calling rmauxSetButtonDownFunc().
2338  *
2339  * since this routine is intended to be called only from within
2340  * rmauxEventLoop, it is not publicly documented.
2341  */
2342 int
RMAUX_BUTTON_FUNC_PARMS()2343 rmauxShiftB2MotionFunc RMAUX_BUTTON_FUNC_PARMS()
2344 {
2345     RMmatrix m;
2346     float xnew;
2347 
2348     rmMatrixIdentity(&m);
2349 
2350     xnew = (float)(xbutton) - x;
2351     xnew *= xscale_delta;
2352     m.m[0][0] = m.m[1][1] = m.m[2][2] = 1.0F + xnew;
2353     rmMatrixMultiply(&saveScaleMatrix,&m,&m);
2354     rmNodeSetScaleMatrix(geomTransformTarget,&m);
2355 
2356 /*    rmFrame(); */
2357     (*renderfunc)(p, private_rmauxGetCurrentSceneGraph());
2358 
2359     /*    fprintf(stderr," scaling \n"); */
2360 
2361     /* foil compiler warning */
2362     ybutton = 0;
2363 
2364     return(1);
2365 }
2366 
2367 int
RMAUX_BUTTON_FUNC_PARMS()2368 rmauxShiftB2UpFunc RMAUX_BUTTON_FUNC_PARMS()
2369 {
2370 #ifdef RM_X
2371     XUndefineCursor(rmxPipeGetDisplay(p),
2372     		    rmPipeGetWindow(p));
2373 #endif
2374     /* foil compiler warning */
2375     xbutton = ybutton = 0;
2376     return(1);
2377 }
2378 
2379 /* PRIVATE (rmauxEventLoop)
2380  *
2381  * convert from X11 style button enumerators to RM enumerators
2382  */
2383 int
private_rmauxButtonNumToIndex(int xbutton_num)2384 private_rmauxButtonNumToIndex (int xbutton_num)
2385 {
2386     int rstat = 0;
2387 
2388     switch(xbutton_num)
2389     {
2390     case Button1:
2391 	rstat = RM_BUTTON1;
2392 	break;
2393 
2394     case Button2:
2395 	rstat = RM_BUTTON2;
2396 	break;
2397 
2398     case Button3:
2399 	rstat = RM_BUTTON3;
2400 	break;
2401 
2402     case Button4:
2403 	rstat = RM_BUTTON4;
2404 	break;
2405 
2406     case Button5:
2407 	rstat = RM_BUTTON5;
2408 	break;
2409     }
2410     return(rstat);
2411 }
2412 
2413 
2414 /* PRIVATE (rmauxEventLoop)
2415  *
2416  * return valid RM key modifier from X11 style button modifier state
2417  */
2418 int
private_rmauxModifierToIndex(int state)2419 private_rmauxModifierToIndex (int state)
2420 {
2421     int rstat = RM_NO_MODIFIER;
2422 
2423     if (state & RM_SHIFT_MODMASK)
2424 	rstat = RM_SHIFT_MODIFIER;
2425     else if (state & RM_CONTROL_MODMASK)
2426 	rstat = RM_CONTROL_MODIFIER;
2427 
2428     return(rstat);
2429 }
2430 
2431 void
private_rmauxSetCurrentSceneGraph(RMnode * n)2432 private_rmauxSetCurrentSceneGraph(RMnode *n)
2433 {
2434     static_sceneGraphHandle = n;
2435 }
2436 
2437 RMnode *
private_rmauxGetCurrentSceneGraph(void)2438 private_rmauxGetCurrentSceneGraph(void)
2439 {
2440     return(static_sceneGraphHandle);
2441 }
2442 
2443 void
private_rmauxSetCameraResizeNode(RMnode * n)2444 private_rmauxSetCameraResizeNode(RMnode *n)
2445 {
2446     static_resizeCameraNode = n;
2447 }
2448 
2449 RMnode *
private_rmauxGetCameraResizeNode(void)2450 private_rmauxGetCameraResizeNode(void)
2451 {
2452     return(static_resizeCameraNode);
2453 }
2454 
2455 static void
private_rmauxDoSpinCallback(void)2456 private_rmauxDoSpinCallback(void)
2457 {
2458     RMmatrix m;
2459     rmNodeGetRotateMatrix(geomTransformTarget, &m);
2460     rmMatrixMultiply(&m, &spinMatrix, &m);
2461     rmNodeSetRotateMatrix(geomTransformTarget, &m);
2462 }
2463 
2464 /*
2465  * ----------------------------------------------------
2466  * @Name rmauxSetSpinEnable
2467  @pstart
2468  void rmauxSetSpinEnable (RMenum spinEnableBoolean)
2469  @pend
2470 
2471  @astart
2472 
2473  RMenum spinEnableBoolean - may be either RM_TRUE or RM_FALSE. When set to
2474     RM_TRUE, "spin mode" will be enabled. When set to RM_FALSE, "spin mode"
2475     is disabled.
2476  @aend
2477 
2478  @dstart
2479 
2480  "Spin mode" refers to auto-rotation. With spin mode enabled, if the
2481  pointer is moving then you release RM_BUTTON2, then the RMnode "target"
2482  will be autorotated until such a time as you again press RM_BUTTON2.
2483  The direction and velocity of rotation is a function of how quickly
2484  the pointer is moving when you release RM_BUTTON2. Internally,
2485  spin mode is implemented by using an idle callback, so auto-spin will
2486  occur only when there are no events to process. The idle callback used
2487  to implement auto-rotation is independent of rmauxSetIdleFunc(), so
2488  you may have an idle function and auto-spin will still be operational.
2489 
2490  Auto-spins are applied to the transformation target set with either
2491  rmauxSetGeomTransform() or rmauxUI(). You may enable auto-spin without
2492  assigning a transformation target, but no auto-spin will occur unless
2493  you have established a transformation target.
2494 
2495  @dend
2496  * ----------------------------------------------------
2497  */
2498 void
rmauxSetSpinEnable(RMenum spinEnableBool)2499 rmauxSetSpinEnable (RMenum spinEnableBool)
2500 {
2501     if ((spinEnableBool != RM_TRUE) && (spinEnableBool != RM_FALSE))
2502     {
2503 	rmWarning("rmauxSetSpinEnable() error: the input spinEnableBool parameter must be either RM_TRUE or RM_FALSE.");
2504 	return;
2505     }
2506 
2507     if ((spinEnableBool == RM_TRUE) && (geomTransformTarget == NULL))
2508 	rmWarning("rmauxSetSpinEnable() warning: you are enabling auto-spins when no transformation target has been established. While this is not an error, no auto-spin will occur unless you establish such a target with either rmauxSetGeomTransform() or rmauxUI(). \n");
2509 
2510     spinModeEnabled = spinEnableBool;
2511 }
2512 
2513 /*
2514  * ----------------------------------------------------
2515  * @Name rmauxGetSpinEnable
2516  @pstart
2517  RMenum rmauxGetSpinEnable (void)
2518  @pend
2519 
2520  @astart
2521 
2522  @aend
2523 
2524  @dstart
2525 
2526  Returns either RM_TRUE or RM_FALSE, reflecting the current state
2527  of whether or not "auto spin" is enabled. See the description of
2528  rmauxSetSpinEnable for more details about auto-spin mode.
2529 
2530  @dend
2531  * ----------------------------------------------------
2532  */
2533 RMenum
rmauxGetSpinEnable(void)2534 rmauxGetSpinEnable (void)
2535 {
2536     return spinModeEnabled;
2537 }
2538 /* EOF */
2539