1 /*
2     A system to permit user selection of a block and rotation axis
3     of a magic cube.
4     Copyright (C) 1998,  2003, 2011  John Darrington
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 3 of the License,  or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include <config.h>
21 
22 #include "control.h"
23 #include <gdk/gdkkeysyms.h>
24 
25 /* This library provides  a means of picking a block using the mouse cursor.
26 
27 Two mutually co-operative mechanisms are used in this library.  There is a
28 timer callback,  which occurs at regular intervals.  There is also the mouse
29 motion callback,  which occurs whenever the mouse cursor is moving.  If two
30 consecutive timer callbacks occur,  without and intervening mouse motion callback,
31 then the cursor is assumed to be stationary.
32 
33 If a stationary mouse is detected,  the program goes on to determine which block
34 in the cube (if any) the cursor is located upon.
35 */
36 
37 #include "select.h"
38 #include <float.h>
39 #include <stdio.h>
40 
41 #include <stdlib.h>
42 
43 #include <gtk/gtk.h>
44 #include <assert.h>
45 
46 #include "drwBlock.h"
47 #include "cubeview.h"
48 #include "glarea.h"
49 
50 static gboolean detect_motion (GtkWidget * w,
51 			       GdkEventMotion * event, gpointer user_data);
52 
53 
54 static gboolean UnsetMotion (gpointer data);
55 
56 
57 struct cublet_selection
58 {
59   guint timer;
60   select_func *action;
61   gpointer data;
62   gint idle_threshold;
63   double granularity;
64   GtkWidget *w;
65 
66   gboolean motion;
67   gboolean stop_detected;
68 
69   /* A copy of the last selection taken */
70   struct facet_selection current_selection;
71 
72   gint mouse_x;
73   gint mouse_y;
74 };
75 
76 
77 
78 static gboolean
key_press(GtkWidget * w,GdkEventKey * e,gpointer data)79 key_press (GtkWidget *w, GdkEventKey *e,  gpointer data)
80 {
81   struct cublet_selection *cs = data;
82 
83   if ( e->keyval != GDK_KEY_Shift_L && e->keyval != GDK_KEY_Shift_R )
84     return FALSE;
85 
86   selection_func (cs, w);
87 
88   return FALSE;
89 }
90 
91 
92 static void pickPolygons (GbkCubeview * cv, struct cublet_selection *cs,
93 			  struct facet_selection *sel);
94 
95 /* Initialise the selection mechanism.  Holdoff is the time for which
96 the mouse must stay still,  for anything to happen. Precision is the
97 minimum distance it must have moved. DO_THIS is a pointer to a function
98 to be called when a new block is selected. DATA is a data to be passed to DO_THIS*/
99 struct cublet_selection *
select_create(GtkWidget * w,int holdoff,double precision,select_func * do_this,gpointer data)100 select_create (GtkWidget * w, int holdoff,
101 	       double precision, select_func * do_this, gpointer data)
102 {
103   struct cublet_selection *cs = g_malloc (sizeof *cs);
104 
105   cs->idle_threshold = holdoff;
106   cs->granularity = precision;
107 
108   g_signal_connect (w, "motion-notify-event", G_CALLBACK (detect_motion), cs);
109 
110   g_signal_connect (w, "key-press-event", G_CALLBACK (key_press), cs);
111   g_signal_connect (w, "key-release-event", G_CALLBACK (key_press), cs);
112 
113   cs->action = do_this;
114   cs->data = data;
115   cs->stop_detected = FALSE;
116   cs->motion = FALSE;
117 
118   cs->timer = g_timeout_add (cs->idle_threshold, UnsetMotion, cs);
119 
120   cs->w = w;
121 
122   cs->current_selection.block = -1;
123   cs->current_selection.face = -1;
124   cs->current_selection.quadrant = -1;
125 
126   return cs;
127 }
128 
129 
130 void
select_destroy(struct cublet_selection * cs)131 select_destroy (struct cublet_selection *cs)
132 {
133   select_disable (cs);
134   g_free (cs);
135 }
136 
137 
138 
139 void
select_disable(struct cublet_selection * cs)140 select_disable (struct cublet_selection *cs)
141 {
142   if (cs->timer)
143     g_source_remove (cs->timer);
144   cs->timer = 0;
145 }
146 
147 void
select_enable(struct cublet_selection * cs)148 select_enable (struct cublet_selection *cs)
149 {
150   if (0 == cs->timer)
151     cs->timer = g_timeout_add (cs->idle_threshold, UnsetMotion, cs);
152 }
153 
154 /* This callback occurs whenever the mouse is moving */
155 static gboolean
detect_motion(GtkWidget * w,GdkEventMotion * event,gpointer data)156 detect_motion (GtkWidget * w, GdkEventMotion * event, gpointer data)
157 {
158   struct cublet_selection *cs = data;
159 
160   if (event->type != GDK_MOTION_NOTIFY)
161     return FALSE;
162 
163   cs->mouse_x = event->x;
164   cs->mouse_y = event->y;
165 
166   cs->motion = TRUE;
167   cs->stop_detected = FALSE;
168 
169   return FALSE;
170 }
171 
172 
173 
174 
175 
176 
177 /* This callback occurs at regular intervals.   The period is determined by
178 cs->idle_threshold.  It checks to see if the mouse has moved,  since the last
179 call of this function.
180 Post-condition:  motion is FALSE.
181 */
182 static gboolean
UnsetMotion(gpointer data)183 UnsetMotion (gpointer data)
184 {
185   struct cublet_selection *cs = data;
186 
187   GbkCubeview *cv = GBK_CUBEVIEW (cs->data);
188 
189   if (cs->motion == FALSE)
190     {				/* if not moved since last time */
191 
192       if (!cs->stop_detected)
193 	{
194 	  /* in here,  things happen upon the mouse stopping */
195 	  cs->stop_detected = TRUE;
196 	  select_update (cv, cs);
197 	}
198     }
199 
200   cs->motion = FALSE;
201 
202   return TRUE;
203 }				/* end UnsetMotion */
204 
205 
206 
207 static int
get_widget_height(GtkWidget * w)208 get_widget_height (GtkWidget * w)
209 {
210   GtkAllocation allocation;
211   gtk_widget_get_allocation (w, &allocation);
212   return allocation.height;
213 }
214 
215 
216 
217 #include "select.h"
218 #include <float.h>
219 #include <stdio.h>
220 
221 #include <assert.h>
222 #include <string.h>
223 #include <GL/glu.h>
224 
225 
226 #define BUFSIZE 512
227 
228 
229 
230 
231 static void choose_items (GLint hits, GLuint buffer[],
232 			  struct facet_selection *);
233 
234 
235 
236 /* Identify the block at screen co-ordinates x,  y .  This func determines all
237    candidate blocks.  That is,  all blocks which orthogonally project to x,  y.  It
238    then calls choose_items,  to determine which of them is closest to the screen.
239 */
240 static void
pickPolygons(GbkCubeview * cv,struct cublet_selection * cs,struct facet_selection * sel)241 pickPolygons (GbkCubeview * cv, struct cublet_selection *cs,
242 	      struct facet_selection *sel)
243 {
244   GLint height;
245 
246   GLint viewport[4];
247   GLuint selectBuf[BUFSIZE];
248   GLint hits;
249 
250   GtkWidget *w;
251 
252   if (!gdk_gl_drawable_make_current (cv->gldrawable, cv->glcontext))
253     {
254       g_critical ("Cannot set gl drawable current\n");
255       return;
256     }
257   assert (cs->granularity > 0);
258   w = cs->w;
259 
260   height = get_widget_height (w);
261 
262   glSelectBuffer (BUFSIZE, selectBuf);
263 
264   glRenderMode (GL_SELECT);
265 
266   glInitNames ();
267   glPushName (0xFFFF);
268 
269   glMatrixMode (GL_PROJECTION);
270   glPushMatrix ();
271   glLoadIdentity ();
272 
273   glGetIntegerv (GL_VIEWPORT, viewport);
274 
275   gluPickMatrix ((GLdouble) cs->mouse_x, (GLdouble) (height - cs->mouse_y),
276 		 cs->granularity, cs->granularity, viewport);
277 
278   perspectiveSet (&cv->scene);
279   gbk_cubeview_model_view_init (cv);
280   drawCube (cv->cube, TRUE, cv);
281   glMatrixMode (GL_PROJECTION);
282   glPopMatrix ();
283 
284   ERR_CHECK ("");
285   hits = glRenderMode (GL_RENDER);
286 
287   choose_items (hits, selectBuf, sel);
288 }
289 
290 
291 
292 
293 
294 /* Find out which of all the objects in buffer is the one with the
295    lowest Z value.  ie. closer in the depth buffer  */
296 static void
choose_items(GLint hits,GLuint buffer[],struct facet_selection * selection)297 choose_items (GLint hits, GLuint buffer[], struct facet_selection *selection)
298 {
299   unsigned int i, j;
300   GLuint names, *ptr;
301 
302   GLint closest[3] = { -1, -1, -1 };
303 
304   float zvalue = FLT_MAX;
305   float z1;
306 
307 #define SEL_BLOCK 0
308 #define SEL_FACE 1
309 #define SEL_QUAD 2
310 
311   g_return_if_fail (hits >= 0);
312 
313   ptr = (GLuint *) buffer;
314 
315   for (i = 0; i < hits; i++)
316     {
317       names = *ptr++;
318 
319       z1 = (float) *ptr++ / 0x7fffffff;
320 
321       ptr++;			/* we're not interested in the minimum zvalue */
322 
323       if (z1 < zvalue)
324 	{
325 	  zvalue = z1;
326 	  for (j = 0; j < names; j++)
327 	    {
328 	      closest[j] = *ptr++;
329 	    }
330 
331 	}
332       else
333 	{
334 	  ptr += names;
335 	}
336     }
337 
338   selection->block = closest[SEL_BLOCK];
339   selection->face = closest[SEL_FACE];
340   selection->quadrant = closest[SEL_QUAD];
341 
342 #if DEBUG
343   fprintf (stderr, "Selected block %d,  face %d,  quadrant %d\n",
344 	   selection->block, selection->face, selection->quadrant);
345 #endif
346 
347 }
348 
349 /* an accessor func to get the value of the currently selected items */
350 const struct facet_selection *
select_get(const struct cublet_selection * cs)351 select_get (const struct cublet_selection *cs)
352 {
353   return &cs->current_selection;
354 }
355 
356 
357 /* This func,  determines which block the mouse is pointing at,  and if it
358    has changed,  calls the function ptr "cs->action" */
359 void
select_update(GbkCubeview * cv,struct cublet_selection * cs)360 select_update (GbkCubeview * cv, struct cublet_selection *cs)
361 {
362   pickPolygons (cv, cs, &cs->current_selection);
363   if (cs->action)
364     cs->action (cs, cs->data);
365 }
366 
367 gboolean
select_is_selected(const struct cublet_selection * cs)368 select_is_selected (const struct cublet_selection *cs)
369 {
370   return cs->current_selection.quadrant != -1;
371 }
372 
373 
374 GtkWidget *
cublet_selection_get_widget(const struct cublet_selection * cs)375 cublet_selection_get_widget (const struct cublet_selection * cs)
376 {
377   return cs->w;
378 }
379