1 /*****************************************************************************
2 * puzzle.c : Puzzle game
3 *****************************************************************************
4 * Copyright (C) 2005-2006 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
33 #include "filter_common.h"
34 #include "vlc_image.h"
35 #include "vlc_input.h"
36 #include "vlc_playlist.h"
38 /*****************************************************************************
40 *****************************************************************************/
41 static int Create ( vlc_object_t * );
42 static void Destroy ( vlc_object_t * );
44 static int Init ( vout_thread_t * );
45 static void End ( vout_thread_t * );
46 static void Render ( vout_thread_t *, picture_t * );
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 * );
53 static int PuzzleCallback( vlc_object_t *, char const *,
54 vlc_value_t, vlc_value_t, void * );
56 /*****************************************************************************
58 *****************************************************************************/
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.")
67 #define CFG_PREFIX "puzzle-"
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 );
76 add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
77 ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE );
79 add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
80 COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
82 add_bool( CFG_PREFIX "black-slot", 0, NULL,
83 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_FALSE );
86 set_callbacks( Create, Destroy );
89 static const char *ppsz_filter_options[] = {
90 "rows", "cols", "black-slot", NULL
93 /*****************************************************************************
94 * vout_sys_t: Magnify video output method descriptor
95 *****************************************************************************/
98 vout_thread_t *p_vout;
100 image_handler_t *p_image;
106 vlc_bool_t b_finished;
108 vlc_bool_t b_blackslot;
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 )
116 return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
119 /*****************************************************************************
121 *****************************************************************************/
122 static vlc_bool_t finished( vout_sys_t *p_sys )
125 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
127 if( i != p_sys->pi_order[i] ) return VLC_FALSE;
131 static vlc_bool_t is_valid( vout_sys_t *p_sys )
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++ )
137 if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
139 d += i / p_sys->i_cols + 1;
142 for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
144 if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
146 if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
149 if( d%2!=0 ) return VLC_FALSE;
150 else return VLC_TRUE;
152 static void shuffle( vout_sys_t *p_sys )
155 free( p_sys->pi_order );
156 p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
159 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
161 p_sys->pi_order[i] = -1;
164 for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
166 i = rand()%( p_sys->i_cols * p_sys->i_rows );
167 if( p_sys->pi_order[i] == -1 )
169 p_sys->pi_order[i] = c;
173 p_sys->b_finished = finished( p_sys );
174 } while( p_sys->b_finished == VLC_TRUE
175 || is_valid( p_sys ) == VLC_FALSE );
177 if( p_sys->b_blackslot == VLC_TRUE )
179 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
181 if( p_sys->pi_order[i] ==
182 p_sys->i_cols * p_sys->i_rows - 1 )
184 p_sys->i_selected = i;
191 p_sys->i_selected = -1;
195 /*****************************************************************************
196 * Create: allocates Magnify video thread output method
197 *****************************************************************************/
198 static int Create( vlc_object_t *p_this )
200 vout_thread_t *p_vout = (vout_thread_t *)p_this;
202 /* Allocate structure */
203 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
204 if( p_vout->p_sys == NULL )
206 msg_Err( p_vout, "out of memory" );
210 p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
212 config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
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 );
228 p_vout->p_sys->pi_order = NULL;
229 shuffle( p_vout->p_sys );
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;
241 /*****************************************************************************
242 * Init: initialize Magnify video thread output method
243 *****************************************************************************/
244 static int Init( vout_thread_t *p_vout )
249 memset( &fmt, 0, sizeof( video_format_t ) );
251 I_OUTPUTPICTURES = 0;
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;
259 p_vout->fmt_out = p_vout->fmt_in;
260 fmt = p_vout->fmt_out;
262 /* Try to open the real video output */
263 msg_Dbg( p_vout, "spawning the real video output" );
265 p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
267 /* Everything failed */
268 if( p_vout->p_sys->p_vout == NULL )
270 msg_Err( p_vout, "cannot open vout, aborting" );
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",
279 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
280 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
281 ADD_PARENT_CALLBACKS( SendEventsToChild );
286 /*****************************************************************************
287 * End: terminate Magnify video thread output method
288 *****************************************************************************/
289 static void End( vout_thread_t *p_vout )
293 /* Free the fake output buffers we allocated */
294 for( i_index = I_OUTPUTPICTURES ; i_index ; )
297 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
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);
305 #define SHUFFLE_WIDTH 81
306 #define SHUFFLE_HEIGHT 13
307 static const char *shuffle_button[] =
309 ".................................................................................",
310 ".............. ............................ ........ ...... ...............",
311 ".............. ........................... ......... ........ ...............",
312 ".............. ........................... ......... ........ ...............",
313 ".. ....... . ....... .... ...... ...... ...... ........ ...",
314 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
315 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
316 ". ....... .... ...... .... ....... ......... ........ ...... .",
317 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
318 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
319 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
320 ".. ....... .... ....... . ....... ......... ........ ........ ..",
321 "................................................................................."};
324 /*****************************************************************************
325 * Destroy: destroy Magnify video thread output method
326 *****************************************************************************/
327 static void Destroy( vlc_object_t *p_this )
329 vout_thread_t *p_vout = (vout_thread_t *)p_this;
331 if( p_vout->p_sys->p_vout )
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 );
338 image_HandlerDelete( p_vout->p_sys->p_image );
339 free( p_vout->p_sys->pi_order );
341 DEL_PARENT_CALLBACKS( SendEventsToChild );
343 free( p_vout->p_sys );
346 /*****************************************************************************
347 * Render: displays previously rendered output
348 *****************************************************************************/
349 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
353 //video_format_t fmt_out;
354 // memset( &fmt_out, 0, sizeof(video_format_t) );
355 //picture_t *p_converted;
359 int i_rows = p_vout->p_sys->i_rows;
360 int i_cols = p_vout->p_sys->i_cols;
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 ) )
366 if( p_vout->b_die || p_vout->b_error )
370 msleep( VOUT_OUTMEM_SLEEP );
373 vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
375 for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
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;
382 for( i = 0; i < i_cols * i_rows; i++ )
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;
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 )
397 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
398 for( ; i_row < i_last_row; i_row++, i_orow++ )
401 pf_memset( p_out->p_pixels + i_row * i_pitch
402 + i_col * i_pitch / i_cols,
403 color, i_pitch / i_cols );
408 for( ; i_row < i_last_row; i_row++, i_orow++ )
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,
421 if( p_vout->p_sys->i_selected != -1
422 && p_vout->p_sys->b_blackslot == VLC_FALSE )
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;
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++ )
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;
445 pf_memset( p_out->p_pixels + i_row * i_pitch
446 + i_col * i_pitch / i_cols,
447 0xff, i_pitch / i_cols );
450 if( p_vout->p_sys->b_finished == VLC_TRUE )
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++ )
457 for( j = 0; j < SHUFFLE_WIDTH; j++ )
459 if( shuffle_button[i][j] == '.' )
460 p_out->p_pixels[ i * i_pitch + j ] = 0xff;
465 vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
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 )
474 var_Set( (vlc_object_t *)p_data, psz_var, newval );
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 )
485 vout_thread_t *p_vout = (vout_thread_t *)p_this;
486 var_Set( p_vout->p_sys->p_vout, psz_var, newval );
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 )
496 vout_thread_t *p_vout = (vout_thread_t*)p_data;
501 #define MOUSE_CLICKED 2
502 #define MOUSE_MOVE_X 4
503 #define MOUSE_MOVE_Y 8
504 #define MOUSE_MOVE 12
507 int v_h = p_vout->output.i_height;
508 int v_w = p_vout->output.i_width;
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;
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" );
520 if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
523 if( mouse & MOUSE_CLICKED )
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 )
529 shuffle( p_vout->p_sys );
531 else if( p_vout->p_sys->i_selected == -1 )
533 p_vout->p_sys->i_selected = i_pos;
535 else if( p_vout->p_sys->i_selected == i_pos
536 && p_vout->p_sys->b_blackslot == VLC_FALSE )
538 p_vout->p_sys->i_selected = -1;
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 )
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;
554 p_vout->p_sys->i_selected = -1;
556 p_vout->p_sys->b_finished = finished( p_vout->p_sys );
562 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
563 vlc_value_t oldval, vlc_value_t newval,
566 vout_sys_t *p_sys = (vout_sys_t *)p_data;
567 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
569 p_sys->i_rows = __MAX( 1, newval.i_int );
571 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
573 p_sys->i_cols = __MAX( 1, newval.i_int );
575 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
577 p_sys->b_blackslot = newval.b_bool;