]> git.sesse.net Git - vlc/blob - modules/video_filter/puzzle.c
Really prevent illegal puzzles and prevent some illegal moves.
[vlc] / modules / video_filter / puzzle.c
1 /*****************************************************************************
2  * puzzle.c : Puzzle game
3  *****************************************************************************
4  * Copyright (C) 2005-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/vout.h>
32
33 #include <math.h>
34
35 #include "filter_common.h"
36 #include "vlc_image.h"
37 #include "vlc_input.h"
38 #include "vlc_playlist.h"
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static int  Create    ( vlc_object_t * );
44 static void Destroy   ( vlc_object_t * );
45
46 static int  Init      ( vout_thread_t * );
47 static void End       ( vout_thread_t * );
48 static void Render    ( vout_thread_t *, picture_t * );
49
50 static int  SendEvents   ( vlc_object_t *, char const *,
51                            vlc_value_t, vlc_value_t, void * );
52 static int  MouseEvent   ( vlc_object_t *, char const *,
53                            vlc_value_t, vlc_value_t, void * );
54
55 /*****************************************************************************
56  * Module descriptor
57  *****************************************************************************/
58
59 #define ROWS_TEXT N_("Number of puzzle rows")
60 #define ROWS_LONGTEXT N_("Number of puzzle rows")
61 #define COLS_TEXT N_("Number of puzzle columns")
62 #define COLS_LONGTEXT N_("Number of puzzle columns")
63 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
64 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
65
66 vlc_module_begin();
67     set_description( _("Puzzle interactive game video filter") );
68     set_shortname( _( "Puzzle" ));
69     set_capability( "video filter", 0 );
70     set_category( CAT_VIDEO );
71     set_subcategory( SUBCAT_VIDEO_VFILTER );
72
73     add_integer_with_range( "puzzle-rows", 4, 1, 128, NULL,
74                             ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE );
75     add_integer_with_range( "puzzle-cols", 4, 1, 128, NULL,
76                             COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
77     add_bool( "puzzle-black-slot", 0, NULL,
78               BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_FALSE );
79
80     set_callbacks( Create, Destroy );
81 vlc_module_end();
82
83 /*****************************************************************************
84  * vout_sys_t: Magnify video output method descriptor
85  *****************************************************************************/
86 struct vout_sys_t
87 {
88     vout_thread_t *p_vout;
89
90     image_handler_t *p_image;
91
92     int i_cols;
93     int i_rows;
94     int *pi_order;
95     int i_selected;
96     vlc_bool_t b_finished;
97
98     vlc_bool_t b_blackslot;
99 };
100
101 /*****************************************************************************
102  * Control: control facility for the vout (forwards to child vout)
103  *****************************************************************************/
104 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
105 {
106     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
107 }
108
109 /*****************************************************************************
110  * Misc stuff...
111  *****************************************************************************/
112 static vlc_bool_t finished( vout_sys_t *p_sys )
113 {
114     int i;
115     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
116     {
117         if( i != p_sys->pi_order[i] ) return VLC_FALSE;
118     }
119     return VLC_TRUE;
120 }
121 static vlc_bool_t is_valid( vout_sys_t *p_sys )
122 {
123     int i, j, d=0;
124     if( p_sys->b_blackslot == VLC_FALSE ) return VLC_TRUE;
125     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
126     {
127         if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
128         {
129             d += i / p_sys->i_cols + 1;
130             continue;
131         }
132         for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
133         {
134             if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
135                 continue;
136             if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
137         }
138     }
139     if( d%2!=0 ) return VLC_FALSE;
140     else return VLC_TRUE;
141 }
142 static void shuffle( vout_sys_t *p_sys )
143 {
144     int i, c;
145     free( p_sys->pi_order );
146     p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
147     do
148     {
149         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
150         {
151             p_sys->pi_order[i] = -1;
152         }
153         i = 0;
154         for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
155         {
156             i = rand()%( p_sys->i_cols * p_sys->i_rows );
157             if( p_sys->pi_order[i] == -1 )
158             {
159                 p_sys->pi_order[i] = c;
160                 c++;
161             }
162         }
163         p_sys->b_finished = finished( p_sys );
164     } while(    p_sys->b_finished == VLC_TRUE
165              || is_valid( p_sys ) == VLC_FALSE );
166
167     if( p_sys->b_blackslot == VLC_TRUE )
168     {
169         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
170         {
171             if( p_sys->pi_order[i] ==
172                 p_sys->i_cols * p_sys->i_rows - 1 )
173             {
174                 p_sys->i_selected = i;
175                 break;
176             }
177         }
178     }
179     else
180     {
181         p_sys->i_selected = -1;
182     }
183 }
184
185 /*****************************************************************************
186  * Create: allocates Magnify video thread output method
187  *****************************************************************************/
188 static int Create( vlc_object_t *p_this )
189 {
190     vout_thread_t *p_vout = (vout_thread_t *)p_this;
191
192     /* Allocate structure */
193     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
194     if( p_vout->p_sys == NULL )
195     {
196         msg_Err( p_vout, "out of memory" );
197         return VLC_ENOMEM;
198     }
199
200     p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
201
202     p_vout->p_sys->i_rows = config_GetInt( p_vout, "puzzle-rows" );
203     p_vout->p_sys->i_cols = config_GetInt( p_vout, "puzzle-cols" );
204     p_vout->p_sys->b_blackslot = config_GetInt( p_vout, "puzzle-black-slot" );
205
206     p_vout->p_sys->pi_order = NULL;
207     shuffle( p_vout->p_sys );
208
209     p_vout->pf_init = Init;
210     p_vout->pf_end = End;
211     p_vout->pf_manage = NULL;
212     p_vout->pf_render = Render;
213     p_vout->pf_display = NULL;
214     p_vout->pf_control = Control;
215
216     return VLC_SUCCESS;
217 }
218
219 /*****************************************************************************
220  * Init: initialize Magnify video thread output method
221  *****************************************************************************/
222 static int Init( vout_thread_t *p_vout )
223 {
224     int i_index;
225     picture_t *p_pic;
226     video_format_t fmt = {0};
227
228     I_OUTPUTPICTURES = 0;
229
230     /* Initialize the output structure */
231     p_vout->output.i_chroma = p_vout->render.i_chroma;
232     p_vout->output.i_width  = p_vout->render.i_width;
233     p_vout->output.i_height = p_vout->render.i_height;
234     p_vout->output.i_aspect = p_vout->render.i_aspect;
235
236     p_vout->fmt_out = p_vout->fmt_in;
237     fmt = p_vout->fmt_out;
238
239     /* Try to open the real video output */
240     msg_Dbg( p_vout, "spawning the real video output" );
241
242     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
243
244     /* Everything failed */
245     if( p_vout->p_sys->p_vout == NULL )
246     {
247         msg_Err( p_vout, "cannot open vout, aborting" );
248         return VLC_EGENERIC;
249     }
250
251     var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
252     var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
253     var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
254                      MouseEvent, p_vout);
255
256     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
257     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
258     ADD_PARENT_CALLBACKS( SendEventsToChild );
259
260     return VLC_SUCCESS;
261 }
262
263 /*****************************************************************************
264  * End: terminate Magnify video thread output method
265  *****************************************************************************/
266 static void End( vout_thread_t *p_vout )
267 {
268     int i_index;
269
270     /* Free the fake output buffers we allocated */
271     for( i_index = I_OUTPUTPICTURES ; i_index ; )
272     {
273         i_index--;
274         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
275     }
276
277     var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
278     var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
279     var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
280 }
281
282 /*****************************************************************************
283  * Destroy: destroy Magnify video thread output method
284  *****************************************************************************/
285 static void Destroy( vlc_object_t *p_this )
286 {
287     vout_thread_t *p_vout = (vout_thread_t *)p_this;
288
289     if( p_vout->p_sys->p_vout )
290     {
291         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
292         vlc_object_detach( p_vout->p_sys->p_vout );
293         vout_Destroy( p_vout->p_sys->p_vout );
294     }
295
296     image_HandlerDelete( p_vout->p_sys->p_image );
297     free( p_vout->p_sys->pi_order );
298
299     DEL_PARENT_CALLBACKS( SendEventsToChild );
300
301     free( p_vout->p_sys );
302 }
303
304 /*****************************************************************************
305  * Render: displays previously rendered output
306  *****************************************************************************/
307 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
308 {
309     picture_t *p_outpic;
310
311     //video_format_t fmt_out = {0};
312     //picture_t *p_converted;
313
314     int i_plane;
315
316     int i_rows = p_vout->p_sys->i_rows;
317     int i_cols = p_vout->p_sys->i_cols;
318
319     /* This is a new frame. Get a structure from the video_output. */
320     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
321               == NULL )
322     {
323         if( p_vout->b_die || p_vout->b_error )
324         {
325             return;
326         }
327         msleep( VOUT_OUTMEM_SLEEP );
328     }
329
330     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
331
332     for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
333     {
334         plane_t *p_in = p_pic->p+i_plane;
335         plane_t *p_out = p_outpic->p+i_plane;
336         int i_pitch = p_in->i_pitch;
337         int i;
338
339         for( i = 0; i < i_cols * i_rows; i++ )
340         {
341             int i_col = i % i_cols;
342             int i_row = i / i_cols;
343             int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
344             int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
345             int i_last_row = i_row + 1;
346             i_orow *= p_in->i_lines / i_rows;
347             i_row *= p_in->i_lines / i_rows;
348             i_last_row *= p_in->i_lines / i_rows;
349
350             if( p_vout->p_sys->b_blackslot == VLC_TRUE
351                 && p_vout->p_sys->b_finished == VLC_FALSE
352                 && i == p_vout->p_sys->i_selected )
353             {
354                 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
355                 for( ; i_row < i_last_row; i_row++, i_orow++ )
356                 {
357                     memset( p_out->p_pixels + i_row * i_pitch
358                                             + i_col * i_pitch / i_cols,
359                             color, i_pitch / i_cols );
360                 }
361             }
362             else
363             {
364                 for( ; i_row < i_last_row; i_row++, i_orow++ )
365                 {
366                     memcpy( p_out->p_pixels + i_row * i_pitch
367                                             + i_col * i_pitch / i_cols,
368                             p_in->p_pixels + i_orow * i_pitch
369                                            + i_ocol * i_pitch / i_cols,
370                             i_pitch / i_cols );
371                 }
372             }
373         }
374     }
375
376     if(    p_vout->p_sys->i_selected != -1
377         && p_vout->p_sys->b_blackslot == VLC_FALSE )
378     {
379         plane_t *p_in = p_pic->p+Y_PLANE;
380         plane_t *p_out = p_outpic->p+Y_PLANE;
381         int i_pitch = p_in->i_pitch;
382         int i_col = p_vout->p_sys->i_selected % i_cols;
383         int i_row = p_vout->p_sys->i_selected / i_cols;
384         int i_last_row = i_row + 1;
385         i_row *= p_in->i_lines / i_rows;
386         i_last_row *= p_in->i_lines / i_rows;
387         memset( p_out->p_pixels + i_row * i_pitch
388                                 + i_col * i_pitch / i_cols,
389                 0xff, i_pitch / i_cols );
390         for( ; i_row < i_last_row; i_row++ )
391         {
392             p_out->p_pixels[   i_row * i_pitch
393                              + i_col * i_pitch / i_cols ] = 0xff;
394             p_out->p_pixels[ i_row * i_pitch
395                              + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
396         }
397         i_row--;
398         memset( p_out->p_pixels + i_row * i_pitch
399                                 + i_col * i_pitch / i_cols,
400                 0xff, i_pitch / i_cols );
401     }
402
403     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
404 }
405
406 /*****************************************************************************
407  * SendEvents: forward mouse and keyboard events to the parent p_vout
408  *****************************************************************************/
409 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
410                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
411 {
412     var_Set( (vlc_object_t *)p_data, psz_var, newval );
413
414     return VLC_SUCCESS;
415 }
416
417 /*****************************************************************************
418  * SendEventsToChild: forward events to the child/children vout
419  *****************************************************************************/
420 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
421                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
422 {
423     vout_thread_t *p_vout = (vout_thread_t *)p_this;
424     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
425     return VLC_SUCCESS;
426 }
427
428 /*****************************************************************************
429  * MouseEvent: callback for mouse events
430  *****************************************************************************/
431 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
432                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
433 {
434     vout_thread_t *p_vout = (vout_thread_t*)p_data;
435     int i_x, i_y;
436     int i_v;
437
438 #define MOUSE_DOWN    1
439 #define MOUSE_CLICKED 2
440 #define MOUSE_MOVE_X  4
441 #define MOUSE_MOVE_Y  8
442 #define MOUSE_MOVE    12
443     uint8_t mouse= 0;
444
445     int v_h = p_vout->output.i_height;
446     int v_w = p_vout->output.i_width;
447     int i_pos;
448
449     if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
450     if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
451     if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
452
453     i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
454     if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
455     i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
456     i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
457
458     if( mouse & MOUSE_CLICKED )
459     {
460         i_pos = p_vout->p_sys->i_cols * ( ( p_vout->p_sys->i_rows * i_y ) / v_h ) + (p_vout->p_sys->i_cols * i_x ) / v_w;
461         if( p_vout->p_sys->b_finished == VLC_TRUE )
462         {
463             shuffle( p_vout->p_sys );
464         }
465         else if( p_vout->p_sys->i_selected == -1 )
466         {
467             p_vout->p_sys->i_selected = i_pos;
468         }
469         else if( p_vout->p_sys->i_selected == i_pos
470                  && p_vout->p_sys->b_blackslot == VLC_FALSE )
471         {
472             p_vout->p_sys->i_selected = -1;
473         }
474         else if(    ( p_vout->p_sys->i_selected == i_pos + 1
475                       && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
476                  || ( p_vout->p_sys->i_selected == i_pos - 1
477                       && i_pos % p_vout->p_sys->i_cols != 0 )
478                  || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
479                  || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
480         {
481             int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
482             p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
483                 p_vout->p_sys->pi_order[ i_pos ];
484             p_vout->p_sys->pi_order[ i_pos ] = a;
485             if( p_vout->p_sys->b_blackslot == VLC_TRUE )
486                 p_vout->p_sys->i_selected = i_pos;
487             else
488                 p_vout->p_sys->i_selected = -1;
489
490             p_vout->p_sys->b_finished = finished( p_vout->p_sys );
491         }
492     }
493     return VLC_SUCCESS;
494 }