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 *****************************************************************************/
34 #include "filter_common.h"
35 #include "vlc_image.h"
36 #include "vlc_input.h"
37 #include "vlc_playlist.h"
39 /*****************************************************************************
41 *****************************************************************************/
42 static int Create ( vlc_object_t * );
43 static void Destroy ( vlc_object_t * );
45 static int Init ( vout_thread_t * );
46 static void End ( vout_thread_t * );
47 static void Render ( vout_thread_t *, picture_t * );
49 static int SendEvents ( vlc_object_t *, char const *,
50 vlc_value_t, vlc_value_t, void * );
51 static int MouseEvent ( vlc_object_t *, char const *,
52 vlc_value_t, vlc_value_t, void * );
54 static int PuzzleCallback( vlc_object_t *, char const *,
55 vlc_value_t, vlc_value_t, void * );
57 /*****************************************************************************
59 *****************************************************************************/
61 #define ROWS_TEXT N_("Number of puzzle rows")
62 #define ROWS_LONGTEXT N_("Number of puzzle rows")
63 #define COLS_TEXT N_("Number of puzzle columns")
64 #define COLS_LONGTEXT N_("Number of puzzle columns")
65 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
66 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
68 #define CFG_PREFIX "puzzle-"
71 set_description( _("Puzzle interactive game video filter") );
72 set_shortname( _( "Puzzle" ));
73 set_capability( "video filter", 0 );
74 set_category( CAT_VIDEO );
75 set_subcategory( SUBCAT_VIDEO_VFILTER );
77 add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
78 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 );
81 add_bool( CFG_PREFIX "black-slot", 0, NULL,
82 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_FALSE );
84 set_callbacks( Create, Destroy );
87 static const char *ppsz_filter_options[] = {
88 "rows", "cols", "black-slot", NULL
91 /*****************************************************************************
92 * vout_sys_t: Magnify video output method descriptor
93 *****************************************************************************/
96 vout_thread_t *p_vout;
98 image_handler_t *p_image;
104 vlc_bool_t b_finished;
106 vlc_bool_t b_blackslot;
109 /*****************************************************************************
110 * Control: control facility for the vout (forwards to child vout)
111 *****************************************************************************/
112 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
114 return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
117 /*****************************************************************************
119 *****************************************************************************/
120 static vlc_bool_t finished( vout_sys_t *p_sys )
123 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
125 if( i != p_sys->pi_order[i] ) return VLC_FALSE;
129 static vlc_bool_t is_valid( vout_sys_t *p_sys )
132 if( p_sys->b_blackslot == VLC_FALSE ) return VLC_TRUE;
133 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
135 if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
137 d += i / p_sys->i_cols + 1;
140 for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
142 if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
144 if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
147 if( d%2!=0 ) return VLC_FALSE;
148 else return VLC_TRUE;
150 static void shuffle( vout_sys_t *p_sys )
153 free( p_sys->pi_order );
154 p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
157 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
159 p_sys->pi_order[i] = -1;
162 for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
164 i = rand()%( p_sys->i_cols * p_sys->i_rows );
165 if( p_sys->pi_order[i] == -1 )
167 p_sys->pi_order[i] = c;
171 p_sys->b_finished = finished( p_sys );
172 } while( p_sys->b_finished == VLC_TRUE
173 || is_valid( p_sys ) == VLC_FALSE );
175 if( p_sys->b_blackslot == VLC_TRUE )
177 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
179 if( p_sys->pi_order[i] ==
180 p_sys->i_cols * p_sys->i_rows - 1 )
182 p_sys->i_selected = i;
189 p_sys->i_selected = -1;
193 /*****************************************************************************
194 * Create: allocates Magnify video thread output method
195 *****************************************************************************/
196 static int Create( vlc_object_t *p_this )
198 vout_thread_t *p_vout = (vout_thread_t *)p_this;
200 /* Allocate structure */
201 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
202 if( p_vout->p_sys == NULL )
204 msg_Err( p_vout, "out of memory" );
208 p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
210 config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
213 p_vout->p_sys->i_rows =
214 var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "rows" );
215 p_vout->p_sys->i_cols =
216 var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "cols" );
217 p_vout->p_sys->b_blackslot =
218 var_CreateGetBoolCommand( p_vout, CFG_PREFIX "black-slot" );
219 var_AddCallback( p_vout, CFG_PREFIX "rows",
220 PuzzleCallback, p_vout->p_sys );
221 var_AddCallback( p_vout, CFG_PREFIX "cols",
222 PuzzleCallback, p_vout->p_sys );
223 var_AddCallback( p_vout, CFG_PREFIX "black-slot",
224 PuzzleCallback, p_vout->p_sys );
226 p_vout->p_sys->pi_order = NULL;
227 shuffle( p_vout->p_sys );
229 p_vout->pf_init = Init;
230 p_vout->pf_end = End;
231 p_vout->pf_manage = NULL;
232 p_vout->pf_render = Render;
233 p_vout->pf_display = NULL;
234 p_vout->pf_control = Control;
239 /*****************************************************************************
240 * Init: initialize Magnify video thread output method
241 *****************************************************************************/
242 static int Init( vout_thread_t *p_vout )
247 memset( &fmt, 0, sizeof( video_format_t ) );
249 I_OUTPUTPICTURES = 0;
251 /* Initialize the output structure */
252 p_vout->output.i_chroma = p_vout->render.i_chroma;
253 p_vout->output.i_width = p_vout->render.i_width;
254 p_vout->output.i_height = p_vout->render.i_height;
255 p_vout->output.i_aspect = p_vout->render.i_aspect;
257 p_vout->fmt_out = p_vout->fmt_in;
258 fmt = p_vout->fmt_out;
260 /* Try to open the real video output */
261 msg_Dbg( p_vout, "spawning the real video output" );
263 p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
265 /* Everything failed */
266 if( p_vout->p_sys->p_vout == NULL )
268 msg_Err( p_vout, "cannot open vout, aborting" );
272 var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
273 var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
274 var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
277 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
278 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
279 ADD_PARENT_CALLBACKS( SendEventsToChild );
284 /*****************************************************************************
285 * End: terminate Magnify video thread output method
286 *****************************************************************************/
287 static void End( vout_thread_t *p_vout )
291 /* Free the fake output buffers we allocated */
292 for( i_index = I_OUTPUTPICTURES ; i_index ; )
295 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
298 var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
299 var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
300 var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
303 #define SHUFFLE_WIDTH 81
304 #define SHUFFLE_HEIGHT 13
305 static const char *shuffle_button[] =
307 ".................................................................................",
308 ".............. ............................ ........ ...... ...............",
309 ".............. ........................... ......... ........ ...............",
310 ".............. ........................... ......... ........ ...............",
311 ".. ....... . ....... .... ...... ...... ...... ........ ...",
312 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
313 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
314 ". ....... .... ...... .... ....... ......... ........ ...... .",
315 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
316 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
317 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
318 ".. ....... .... ....... . ....... ......... ........ ........ ..",
319 "................................................................................."};
322 /*****************************************************************************
323 * Destroy: destroy Magnify video thread output method
324 *****************************************************************************/
325 static void Destroy( vlc_object_t *p_this )
327 vout_thread_t *p_vout = (vout_thread_t *)p_this;
329 if( p_vout->p_sys->p_vout )
331 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
332 vlc_object_detach( p_vout->p_sys->p_vout );
333 vout_Destroy( p_vout->p_sys->p_vout );
336 image_HandlerDelete( p_vout->p_sys->p_image );
337 free( p_vout->p_sys->pi_order );
339 DEL_PARENT_CALLBACKS( SendEventsToChild );
341 free( p_vout->p_sys );
344 /*****************************************************************************
345 * Render: displays previously rendered output
346 *****************************************************************************/
347 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
351 //video_format_t fmt_out;
352 // memset( &fmt_out, 0, sizeof(video_format_t) );
353 //picture_t *p_converted;
357 int i_rows = p_vout->p_sys->i_rows;
358 int i_cols = p_vout->p_sys->i_cols;
360 /* This is a new frame. Get a structure from the video_output. */
361 while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
364 if( p_vout->b_die || p_vout->b_error )
368 msleep( VOUT_OUTMEM_SLEEP );
371 vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
373 for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
375 plane_t *p_in = p_pic->p+i_plane;
376 plane_t *p_out = p_outpic->p+i_plane;
377 int i_pitch = p_in->i_pitch;
380 for( i = 0; i < i_cols * i_rows; i++ )
382 int i_col = i % i_cols;
383 int i_row = i / i_cols;
384 int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
385 int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
386 int i_last_row = i_row + 1;
387 i_orow *= p_in->i_lines / i_rows;
388 i_row *= p_in->i_lines / i_rows;
389 i_last_row *= p_in->i_lines / i_rows;
391 if( p_vout->p_sys->b_blackslot == VLC_TRUE
392 && p_vout->p_sys->b_finished == VLC_FALSE
393 && i == p_vout->p_sys->i_selected )
395 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
396 for( ; i_row < i_last_row; i_row++, i_orow++ )
399 pf_memset( p_out->p_pixels + i_row * i_pitch
400 + i_col * i_pitch / i_cols,
401 color, i_pitch / i_cols );
406 for( ; i_row < i_last_row; i_row++, i_orow++ )
409 pf_memcpy( p_out->p_pixels + i_row * i_pitch
410 + i_col * i_pitch / i_cols,
411 p_in->p_pixels + i_orow * i_pitch
412 + i_ocol * i_pitch / i_cols,
419 if( p_vout->p_sys->i_selected != -1
420 && p_vout->p_sys->b_blackslot == VLC_FALSE )
422 plane_t *p_in = p_pic->p+Y_PLANE;
423 plane_t *p_out = p_outpic->p+Y_PLANE;
424 int i_pitch = p_in->i_pitch;
425 int i_col = p_vout->p_sys->i_selected % i_cols;
426 int i_row = p_vout->p_sys->i_selected / i_cols;
427 int i_last_row = i_row + 1;
428 i_row *= p_in->i_lines / i_rows;
429 i_last_row *= p_in->i_lines / i_rows;
431 pf_memset( p_out->p_pixels + i_row * i_pitch
432 + i_col * i_pitch / i_cols,
433 0xff, i_pitch / i_cols );
434 for( ; i_row < i_last_row; i_row++ )
436 p_out->p_pixels[ i_row * i_pitch
437 + i_col * i_pitch / i_cols ] = 0xff;
438 p_out->p_pixels[ i_row * i_pitch
439 + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
443 pf_memset( p_out->p_pixels + i_row * i_pitch
444 + i_col * i_pitch / i_cols,
445 0xff, i_pitch / i_cols );
448 if( p_vout->p_sys->b_finished == VLC_TRUE )
451 plane_t *p_out = p_outpic->p+Y_PLANE;
452 int i_pitch = p_out->i_pitch;
453 for( i = 0; i < SHUFFLE_HEIGHT; i++ )
455 for( j = 0; j < SHUFFLE_WIDTH; j++ )
457 if( shuffle_button[i][j] == '.' )
458 p_out->p_pixels[ i * i_pitch + j ] = 0xff;
463 vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
466 /*****************************************************************************
467 * SendEvents: forward mouse and keyboard events to the parent p_vout
468 *****************************************************************************/
469 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
470 vlc_value_t oldval, vlc_value_t newval, void *p_data )
472 var_Set( (vlc_object_t *)p_data, psz_var, newval );
477 /*****************************************************************************
478 * SendEventsToChild: forward events to the child/children vout
479 *****************************************************************************/
480 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
481 vlc_value_t oldval, vlc_value_t newval, void *p_data )
483 vout_thread_t *p_vout = (vout_thread_t *)p_this;
484 var_Set( p_vout->p_sys->p_vout, psz_var, newval );
488 /*****************************************************************************
489 * MouseEvent: callback for mouse events
490 *****************************************************************************/
491 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
492 vlc_value_t oldval, vlc_value_t newval, void *p_data )
494 vout_thread_t *p_vout = (vout_thread_t*)p_data;
499 #define MOUSE_CLICKED 2
500 #define MOUSE_MOVE_X 4
501 #define MOUSE_MOVE_Y 8
502 #define MOUSE_MOVE 12
505 int v_h = p_vout->output.i_height;
506 int v_w = p_vout->output.i_width;
509 if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
510 if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
511 if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
513 i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
514 if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
515 i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
516 i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
518 if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
521 if( mouse & MOUSE_CLICKED )
523 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;
524 if( p_vout->p_sys->b_finished == VLC_TRUE
525 && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
527 shuffle( p_vout->p_sys );
529 else if( p_vout->p_sys->i_selected == -1 )
531 p_vout->p_sys->i_selected = i_pos;
533 else if( p_vout->p_sys->i_selected == i_pos
534 && p_vout->p_sys->b_blackslot == VLC_FALSE )
536 p_vout->p_sys->i_selected = -1;
538 else if( ( p_vout->p_sys->i_selected == i_pos + 1
539 && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
540 || ( p_vout->p_sys->i_selected == i_pos - 1
541 && i_pos % p_vout->p_sys->i_cols != 0 )
542 || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
543 || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
545 int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
546 p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
547 p_vout->p_sys->pi_order[ i_pos ];
548 p_vout->p_sys->pi_order[ i_pos ] = a;
549 if( p_vout->p_sys->b_blackslot == VLC_TRUE )
550 p_vout->p_sys->i_selected = i_pos;
552 p_vout->p_sys->i_selected = -1;
554 p_vout->p_sys->b_finished = finished( p_vout->p_sys );
560 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
561 vlc_value_t oldval, vlc_value_t newval,
564 vout_sys_t *p_sys = (vout_sys_t *)p_data;
565 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
567 p_sys->i_rows = __MAX( 1, newval.i_int );
569 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
571 p_sys->i_cols = __MAX( 1, newval.i_int );
573 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
575 p_sys->b_blackslot = newval.b_bool;