]> git.sesse.net Git - vlc/blob - modules/video_filter/puzzle.c
input options whitelisting, step 2 (refs #1371)
[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
28 #include <vlc/vlc.h>
29 #include <vlc_vout.h>
30
31 #include <math.h>
32
33 #include "filter_common.h"
34 #include "vlc_image.h"
35 #include "vlc_input.h"
36 #include "vlc_playlist.h"
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static int  Create    ( vlc_object_t * );
42 static void Destroy   ( vlc_object_t * );
43
44 static int  Init      ( vout_thread_t * );
45 static void End       ( vout_thread_t * );
46 static void Render    ( vout_thread_t *, picture_t * );
47
48 static int  SendEvents   ( vlc_object_t *, char const *,
49                            vlc_value_t, vlc_value_t, void * );
50 static int  MouseEvent   ( vlc_object_t *, char const *,
51                            vlc_value_t, vlc_value_t, void * );
52
53 static int PuzzleCallback( vlc_object_t *, char const *,
54                            vlc_value_t, vlc_value_t, void * );
55
56 /*****************************************************************************
57  * Module descriptor
58  *****************************************************************************/
59
60 #define ROWS_TEXT N_("Number of puzzle rows")
61 #define ROWS_LONGTEXT N_("Number of puzzle rows")
62 #define COLS_TEXT N_("Number of puzzle columns")
63 #define COLS_LONGTEXT N_("Number of puzzle columns")
64 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
65 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
66
67 #define CFG_PREFIX "puzzle-"
68
69 vlc_module_begin();
70     set_description( _("Puzzle interactive game video filter") );
71     set_shortname( _( "Puzzle" ));
72     set_capability( "video filter", 0 );
73     set_category( CAT_VIDEO );
74     set_subcategory( SUBCAT_VIDEO_VFILTER );
75
76     add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
77                             ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE );
78         change_safe();
79     add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
80                             COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
81         change_safe();
82     add_bool( CFG_PREFIX "black-slot", 0, NULL,
83               BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_FALSE );
84         change_safe();
85
86     set_callbacks( Create, Destroy );
87 vlc_module_end();
88
89 static const char *ppsz_filter_options[] = {
90     "rows", "cols", "black-slot", NULL
91 };
92
93 /*****************************************************************************
94  * vout_sys_t: Magnify video output method descriptor
95  *****************************************************************************/
96 struct vout_sys_t
97 {
98     vout_thread_t *p_vout;
99
100     image_handler_t *p_image;
101
102     int i_cols;
103     int i_rows;
104     int *pi_order;
105     int i_selected;
106     vlc_bool_t b_finished;
107
108     vlc_bool_t b_blackslot;
109 };
110
111 /*****************************************************************************
112  * Control: control facility for the vout (forwards to child vout)
113  *****************************************************************************/
114 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
115 {
116     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
117 }
118
119 /*****************************************************************************
120  * Misc stuff...
121  *****************************************************************************/
122 static vlc_bool_t finished( vout_sys_t *p_sys )
123 {
124     int i;
125     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
126     {
127         if( i != p_sys->pi_order[i] ) return VLC_FALSE;
128     }
129     return VLC_TRUE;
130 }
131 static vlc_bool_t is_valid( vout_sys_t *p_sys )
132 {
133     int i, j, d=0;
134     if( p_sys->b_blackslot == VLC_FALSE ) return VLC_TRUE;
135     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
136     {
137         if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
138         {
139             d += i / p_sys->i_cols + 1;
140             continue;
141         }
142         for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
143         {
144             if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
145                 continue;
146             if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
147         }
148     }
149     if( d%2!=0 ) return VLC_FALSE;
150     else return VLC_TRUE;
151 }
152 static void shuffle( vout_sys_t *p_sys )
153 {
154     int i, c;
155     free( p_sys->pi_order );
156     p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
157     do
158     {
159         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
160         {
161             p_sys->pi_order[i] = -1;
162         }
163         i = 0;
164         for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
165         {
166             i = rand()%( p_sys->i_cols * p_sys->i_rows );
167             if( p_sys->pi_order[i] == -1 )
168             {
169                 p_sys->pi_order[i] = c;
170                 c++;
171             }
172         }
173         p_sys->b_finished = finished( p_sys );
174     } while(    p_sys->b_finished == VLC_TRUE
175              || is_valid( p_sys ) == VLC_FALSE );
176
177     if( p_sys->b_blackslot == VLC_TRUE )
178     {
179         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
180         {
181             if( p_sys->pi_order[i] ==
182                 p_sys->i_cols * p_sys->i_rows - 1 )
183             {
184                 p_sys->i_selected = i;
185                 break;
186             }
187         }
188     }
189     else
190     {
191         p_sys->i_selected = -1;
192     }
193 }
194
195 /*****************************************************************************
196  * Create: allocates Magnify video thread output method
197  *****************************************************************************/
198 static int Create( vlc_object_t *p_this )
199 {
200     vout_thread_t *p_vout = (vout_thread_t *)p_this;
201
202     /* Allocate structure */
203     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
204     if( p_vout->p_sys == NULL )
205     {
206         msg_Err( p_vout, "out of memory" );
207         return VLC_ENOMEM;
208     }
209
210     p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
211
212     config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
213                        p_vout->p_cfg );
214
215     p_vout->p_sys->i_rows =
216         var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "rows" );
217     p_vout->p_sys->i_cols =
218         var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "cols" );
219     p_vout->p_sys->b_blackslot =
220         var_CreateGetBoolCommand( p_vout, CFG_PREFIX "black-slot" );
221     var_AddCallback( p_vout, CFG_PREFIX "rows",
222                      PuzzleCallback, p_vout->p_sys );
223     var_AddCallback( p_vout, CFG_PREFIX "cols",
224                      PuzzleCallback, p_vout->p_sys );
225     var_AddCallback( p_vout, CFG_PREFIX "black-slot",
226                      PuzzleCallback, p_vout->p_sys );
227
228     p_vout->p_sys->pi_order = NULL;
229     shuffle( p_vout->p_sys );
230
231     p_vout->pf_init = Init;
232     p_vout->pf_end = End;
233     p_vout->pf_manage = NULL;
234     p_vout->pf_render = Render;
235     p_vout->pf_display = NULL;
236     p_vout->pf_control = Control;
237
238     return VLC_SUCCESS;
239 }
240
241 /*****************************************************************************
242  * Init: initialize Magnify video thread output method
243  *****************************************************************************/
244 static int Init( vout_thread_t *p_vout )
245 {
246     int i_index;
247     picture_t *p_pic;
248     video_format_t fmt;
249     memset( &fmt, 0, sizeof( video_format_t ) );
250
251     I_OUTPUTPICTURES = 0;
252
253     /* Initialize the output structure */
254     p_vout->output.i_chroma = p_vout->render.i_chroma;
255     p_vout->output.i_width  = p_vout->render.i_width;
256     p_vout->output.i_height = p_vout->render.i_height;
257     p_vout->output.i_aspect = p_vout->render.i_aspect;
258
259     p_vout->fmt_out = p_vout->fmt_in;
260     fmt = p_vout->fmt_out;
261
262     /* Try to open the real video output */
263     msg_Dbg( p_vout, "spawning the real video output" );
264
265     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
266
267     /* Everything failed */
268     if( p_vout->p_sys->p_vout == NULL )
269     {
270         msg_Err( p_vout, "cannot open vout, aborting" );
271         return VLC_EGENERIC;
272     }
273
274     var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
275     var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
276     var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
277                      MouseEvent, p_vout);
278
279     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
280     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
281     ADD_PARENT_CALLBACKS( SendEventsToChild );
282
283     return VLC_SUCCESS;
284 }
285
286 /*****************************************************************************
287  * End: terminate Magnify video thread output method
288  *****************************************************************************/
289 static void End( vout_thread_t *p_vout )
290 {
291     int i_index;
292
293     /* Free the fake output buffers we allocated */
294     for( i_index = I_OUTPUTPICTURES ; i_index ; )
295     {
296         i_index--;
297         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
298     }
299
300     var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
301     var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
302     var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
303 }
304
305 #define SHUFFLE_WIDTH 81
306 #define SHUFFLE_HEIGHT 13
307 static const char *shuffle_button[] =
308 {
309 ".................................................................................",
310 "..............  ............................   ........   ......  ...............",
311 "..............  ...........................  .........  ........  ...............",
312 "..............  ...........................  .........  ........  ...............",
313 "..     .......  .    .......  ....  ......     ......     ......  ........    ...",
314 ".  .... ......   ...  ......  ....  .......  .........  ........  .......  ..  ..",
315 ".  ...........  ....  ......  ....  .......  .........  ........  ......  ....  .",
316 ".      .......  ....  ......  ....  .......  .........  ........  ......        .",
317 "..      ......  ....  ......  ....  .......  .........  ........  ......  .......",
318 "......  ......  ....  ......  ....  .......  .........  ........  ......  .......",
319 ". ....  ......  ....  ......  ...   .......  .........  ........  .......  .... .",
320 "..     .......  ....  .......    .  .......  .........  ........  ........     ..",
321 "................................................................................."};
322
323
324 /*****************************************************************************
325  * Destroy: destroy Magnify video thread output method
326  *****************************************************************************/
327 static void Destroy( vlc_object_t *p_this )
328 {
329     vout_thread_t *p_vout = (vout_thread_t *)p_this;
330
331     if( p_vout->p_sys->p_vout )
332     {
333         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
334         vlc_object_detach( p_vout->p_sys->p_vout );
335         vout_Destroy( p_vout->p_sys->p_vout );
336     }
337
338     image_HandlerDelete( p_vout->p_sys->p_image );
339     free( p_vout->p_sys->pi_order );
340
341     DEL_PARENT_CALLBACKS( SendEventsToChild );
342
343     free( p_vout->p_sys );
344 }
345
346 /*****************************************************************************
347  * Render: displays previously rendered output
348  *****************************************************************************/
349 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
350 {
351     picture_t *p_outpic;
352
353     //video_format_t fmt_out;
354     // memset( &fmt_out, 0, sizeof(video_format_t) );
355     //picture_t *p_converted;
356
357     int i_plane;
358
359     int i_rows = p_vout->p_sys->i_rows;
360     int i_cols = p_vout->p_sys->i_cols;
361
362     /* This is a new frame. Get a structure from the video_output. */
363     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
364               == NULL )
365     {
366         if( p_vout->b_die || p_vout->b_error )
367         {
368             return;
369         }
370         msleep( VOUT_OUTMEM_SLEEP );
371     }
372
373     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
374
375     for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
376     {
377         plane_t *p_in = p_pic->p+i_plane;
378         plane_t *p_out = p_outpic->p+i_plane;
379         int i_pitch = p_in->i_pitch;
380         int i;
381
382         for( i = 0; i < i_cols * i_rows; i++ )
383         {
384             int i_col = i % i_cols;
385             int i_row = i / i_cols;
386             int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
387             int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
388             int i_last_row = i_row + 1;
389             i_orow *= p_in->i_lines / i_rows;
390             i_row *= p_in->i_lines / i_rows;
391             i_last_row *= p_in->i_lines / i_rows;
392
393             if( p_vout->p_sys->b_blackslot == VLC_TRUE
394                 && p_vout->p_sys->b_finished == VLC_FALSE
395                 && i == p_vout->p_sys->i_selected )
396             {
397                 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
398                 for( ; i_row < i_last_row; i_row++, i_orow++ )
399                 {
400                     p_vout->p_libvlc->
401                     pf_memset( p_out->p_pixels + i_row * i_pitch
402                                                + i_col * i_pitch / i_cols,
403                                color, i_pitch / i_cols );
404                 }
405             }
406             else
407             {
408                 for( ; i_row < i_last_row; i_row++, i_orow++ )
409                 {
410                     p_vout->p_libvlc->
411                     pf_memcpy( p_out->p_pixels + i_row * i_pitch
412                                                + i_col * i_pitch / i_cols,
413                                p_in->p_pixels + i_orow * i_pitch
414                                               + i_ocol * i_pitch / i_cols,
415                                i_pitch / i_cols );
416                 }
417             }
418         }
419     }
420
421     if(    p_vout->p_sys->i_selected != -1
422         && p_vout->p_sys->b_blackslot == VLC_FALSE )
423     {
424         plane_t *p_in = p_pic->p+Y_PLANE;
425         plane_t *p_out = p_outpic->p+Y_PLANE;
426         int i_pitch = p_in->i_pitch;
427         int i_col = p_vout->p_sys->i_selected % i_cols;
428         int i_row = p_vout->p_sys->i_selected / i_cols;
429         int i_last_row = i_row + 1;
430         i_row *= p_in->i_lines / i_rows;
431         i_last_row *= p_in->i_lines / i_rows;
432         p_vout->p_libvlc->
433         pf_memset( p_out->p_pixels + i_row * i_pitch
434                                    + i_col * i_pitch / i_cols,
435                    0xff, i_pitch / i_cols );
436         for( ; i_row < i_last_row; i_row++ )
437         {
438             p_out->p_pixels[   i_row * i_pitch
439                              + i_col * i_pitch / i_cols ] = 0xff;
440             p_out->p_pixels[ i_row * i_pitch
441                              + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
442         }
443         i_row--;
444         p_vout->p_libvlc->
445         pf_memset( p_out->p_pixels + i_row * i_pitch
446                                    + i_col * i_pitch / i_cols,
447                    0xff, i_pitch / i_cols );
448     }
449
450     if( p_vout->p_sys->b_finished == VLC_TRUE )
451     {
452         int i, j;
453         plane_t *p_out = p_outpic->p+Y_PLANE;
454         int i_pitch = p_out->i_pitch;
455         for( i = 0; i < SHUFFLE_HEIGHT; i++ )
456         {
457             for( j = 0; j < SHUFFLE_WIDTH; j++ )
458             {
459                 if( shuffle_button[i][j] == '.' )
460                    p_out->p_pixels[ i * i_pitch + j ] = 0xff;
461             }
462         }
463     }
464
465     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
466 }
467
468 /*****************************************************************************
469  * SendEvents: forward mouse and keyboard events to the parent p_vout
470  *****************************************************************************/
471 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
472                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
473 {
474     var_Set( (vlc_object_t *)p_data, psz_var, newval );
475
476     return VLC_SUCCESS;
477 }
478
479 /*****************************************************************************
480  * SendEventsToChild: forward events to the child/children vout
481  *****************************************************************************/
482 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
483                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
484 {
485     vout_thread_t *p_vout = (vout_thread_t *)p_this;
486     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
487     return VLC_SUCCESS;
488 }
489
490 /*****************************************************************************
491  * MouseEvent: callback for mouse events
492  *****************************************************************************/
493 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
494                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
495 {
496     vout_thread_t *p_vout = (vout_thread_t*)p_data;
497     int i_x, i_y;
498     int i_v;
499
500 #define MOUSE_DOWN    1
501 #define MOUSE_CLICKED 2
502 #define MOUSE_MOVE_X  4
503 #define MOUSE_MOVE_Y  8
504 #define MOUSE_MOVE    12
505     uint8_t mouse= 0;
506
507     int v_h = p_vout->output.i_height;
508     int v_w = p_vout->output.i_width;
509     int i_pos;
510
511     if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
512     if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
513     if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
514
515     i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
516     if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
517     i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
518     i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
519
520     if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
521         return VLC_SUCCESS;
522
523     if( mouse & MOUSE_CLICKED )
524     {
525         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;
526         if( p_vout->p_sys->b_finished == VLC_TRUE
527             && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
528         {
529             shuffle( p_vout->p_sys );
530         }
531         else if( p_vout->p_sys->i_selected == -1 )
532         {
533             p_vout->p_sys->i_selected = i_pos;
534         }
535         else if( p_vout->p_sys->i_selected == i_pos
536                  && p_vout->p_sys->b_blackslot == VLC_FALSE )
537         {
538             p_vout->p_sys->i_selected = -1;
539         }
540         else if(    ( p_vout->p_sys->i_selected == i_pos + 1
541                       && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
542                  || ( p_vout->p_sys->i_selected == i_pos - 1
543                       && i_pos % p_vout->p_sys->i_cols != 0 )
544                  || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
545                  || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
546         {
547             int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
548             p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
549                 p_vout->p_sys->pi_order[ i_pos ];
550             p_vout->p_sys->pi_order[ i_pos ] = a;
551             if( p_vout->p_sys->b_blackslot == VLC_TRUE )
552                 p_vout->p_sys->i_selected = i_pos;
553             else
554                 p_vout->p_sys->i_selected = -1;
555
556             p_vout->p_sys->b_finished = finished( p_vout->p_sys );
557         }
558     }
559     return VLC_SUCCESS;
560 }
561
562 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
563                            vlc_value_t oldval, vlc_value_t newval,
564                            void *p_data )
565 {
566     vout_sys_t *p_sys = (vout_sys_t *)p_data;
567     if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
568     {
569         p_sys->i_rows = __MAX( 1, newval.i_int );
570     }
571     else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
572     {
573         p_sys->i_cols = __MAX( 1, newval.i_int );
574     }
575     else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
576     {
577         p_sys->b_blackslot = newval.b_bool;
578     }
579     shuffle( p_sys );
580     return VLC_SUCCESS;
581 }