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 *****************************************************************************/
37 #include "filter_common.h"
38 #include "vlc_image.h"
39 #include "vlc_input.h"
40 #include "vlc_playlist.h"
42 /*****************************************************************************
44 *****************************************************************************/
45 static int Create ( vlc_object_t * );
46 static void Destroy ( vlc_object_t * );
48 static int Init ( vout_thread_t * );
49 static void End ( vout_thread_t * );
50 static void Render ( vout_thread_t *, picture_t * );
52 static int SendEvents ( vlc_object_t *, char const *,
53 vlc_value_t, vlc_value_t, void * );
54 static int MouseEvent ( vlc_object_t *, char const *,
55 vlc_value_t, vlc_value_t, void * );
57 static int PuzzleCallback( vlc_object_t *, char const *,
58 vlc_value_t, vlc_value_t, void * );
60 /*****************************************************************************
62 *****************************************************************************/
64 #define ROWS_TEXT N_("Number of puzzle rows")
65 #define ROWS_LONGTEXT N_("Number of puzzle rows")
66 #define COLS_TEXT N_("Number of puzzle columns")
67 #define COLS_LONGTEXT N_("Number of puzzle columns")
68 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
69 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
71 #define CFG_PREFIX "puzzle-"
74 set_description( _("Puzzle interactive game video filter") );
75 set_shortname( _( "Puzzle" ));
76 set_capability( "video filter", 0 );
77 set_category( CAT_VIDEO );
78 set_subcategory( SUBCAT_VIDEO_VFILTER );
80 add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
81 ROWS_TEXT, ROWS_LONGTEXT, false );
82 add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
83 COLS_TEXT, COLS_LONGTEXT, false );
84 add_bool( CFG_PREFIX "black-slot", 0, NULL,
85 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, false );
87 set_callbacks( Create, Destroy );
90 static const char *ppsz_filter_options[] = {
91 "rows", "cols", "black-slot", NULL
94 /*****************************************************************************
95 * vout_sys_t: Magnify video output method descriptor
96 *****************************************************************************/
99 vout_thread_t *p_vout;
101 image_handler_t *p_image;
112 /*****************************************************************************
113 * Control: control facility for the vout (forwards to child vout)
114 *****************************************************************************/
115 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
117 return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
120 /*****************************************************************************
122 *****************************************************************************/
123 static bool finished( vout_sys_t *p_sys )
126 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
128 if( i != p_sys->pi_order[i] ) return false;
132 static bool is_valid( vout_sys_t *p_sys )
135 if( p_sys->b_blackslot == false ) return true;
136 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
138 if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
140 d += i / p_sys->i_cols + 1;
143 for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
145 if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
147 if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
150 if( d%2!=0 ) return false;
153 static void shuffle( vout_sys_t *p_sys )
156 free( p_sys->pi_order );
157 p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
160 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
162 p_sys->pi_order[i] = -1;
165 for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
167 i = rand()%( p_sys->i_cols * p_sys->i_rows );
168 if( p_sys->pi_order[i] == -1 )
170 p_sys->pi_order[i] = c;
174 p_sys->b_finished = finished( p_sys );
175 } while( p_sys->b_finished == true
176 || is_valid( p_sys ) == false );
178 if( p_sys->b_blackslot == true )
180 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
182 if( p_sys->pi_order[i] ==
183 p_sys->i_cols * p_sys->i_rows - 1 )
185 p_sys->i_selected = i;
192 p_sys->i_selected = -1;
196 /*****************************************************************************
197 * Create: allocates Magnify video thread output method
198 *****************************************************************************/
199 static int Create( vlc_object_t *p_this )
201 vout_thread_t *p_vout = (vout_thread_t *)p_this;
203 /* Allocate structure */
204 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
205 if( p_vout->p_sys == NULL )
207 msg_Err( p_vout, "out of memory" );
211 p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
213 config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
216 p_vout->p_sys->i_rows =
217 var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "rows" );
218 p_vout->p_sys->i_cols =
219 var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "cols" );
220 p_vout->p_sys->b_blackslot =
221 var_CreateGetBoolCommand( p_vout, CFG_PREFIX "black-slot" );
222 var_AddCallback( p_vout, CFG_PREFIX "rows",
223 PuzzleCallback, p_vout->p_sys );
224 var_AddCallback( p_vout, CFG_PREFIX "cols",
225 PuzzleCallback, p_vout->p_sys );
226 var_AddCallback( p_vout, CFG_PREFIX "black-slot",
227 PuzzleCallback, p_vout->p_sys );
229 p_vout->p_sys->pi_order = NULL;
230 shuffle( p_vout->p_sys );
232 p_vout->pf_init = Init;
233 p_vout->pf_end = End;
234 p_vout->pf_manage = NULL;
235 p_vout->pf_render = Render;
236 p_vout->pf_display = NULL;
237 p_vout->pf_control = Control;
242 /*****************************************************************************
243 * Init: initialize Magnify video thread output method
244 *****************************************************************************/
245 static int Init( vout_thread_t *p_vout )
250 memset( &fmt, 0, sizeof( video_format_t ) );
252 I_OUTPUTPICTURES = 0;
254 /* Initialize the output structure */
255 p_vout->output.i_chroma = p_vout->render.i_chroma;
256 p_vout->output.i_width = p_vout->render.i_width;
257 p_vout->output.i_height = p_vout->render.i_height;
258 p_vout->output.i_aspect = p_vout->render.i_aspect;
260 p_vout->fmt_out = p_vout->fmt_in;
261 fmt = p_vout->fmt_out;
263 /* Try to open the real video output */
264 msg_Dbg( p_vout, "spawning the real video output" );
266 p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
268 /* Everything failed */
269 if( p_vout->p_sys->p_vout == NULL )
271 msg_Err( p_vout, "cannot open vout, aborting" );
275 var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
276 var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
277 var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
280 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
281 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
282 ADD_PARENT_CALLBACKS( SendEventsToChild );
287 /*****************************************************************************
288 * End: terminate Magnify video thread output method
289 *****************************************************************************/
290 static void End( vout_thread_t *p_vout )
294 /* Free the fake output buffers we allocated */
295 for( i_index = I_OUTPUTPICTURES ; i_index ; )
298 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
301 var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
302 var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
303 var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
306 #define SHUFFLE_WIDTH 81
307 #define SHUFFLE_HEIGHT 13
308 static const char *shuffle_button[] =
310 ".................................................................................",
311 ".............. ............................ ........ ...... ...............",
312 ".............. ........................... ......... ........ ...............",
313 ".............. ........................... ......... ........ ...............",
314 ".. ....... . ....... .... ...... ...... ...... ........ ...",
315 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
316 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
317 ". ....... .... ...... .... ....... ......... ........ ...... .",
318 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
319 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
320 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
321 ".. ....... .... ....... . ....... ......... ........ ........ ..",
322 "................................................................................."};
325 /*****************************************************************************
326 * Destroy: destroy Magnify video thread output method
327 *****************************************************************************/
328 static void Destroy( vlc_object_t *p_this )
330 vout_thread_t *p_vout = (vout_thread_t *)p_this;
332 if( p_vout->p_sys->p_vout )
334 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
335 vlc_object_detach( p_vout->p_sys->p_vout );
336 vout_Destroy( p_vout->p_sys->p_vout );
339 image_HandlerDelete( p_vout->p_sys->p_image );
340 free( p_vout->p_sys->pi_order );
342 DEL_PARENT_CALLBACKS( SendEventsToChild );
344 free( p_vout->p_sys );
347 /*****************************************************************************
348 * Render: displays previously rendered output
349 *****************************************************************************/
350 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
354 //video_format_t fmt_out;
355 // memset( &fmt_out, 0, sizeof(video_format_t) );
356 //picture_t *p_converted;
360 int i_rows = p_vout->p_sys->i_rows;
361 int i_cols = p_vout->p_sys->i_cols;
363 /* This is a new frame. Get a structure from the video_output. */
364 while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
367 if( p_vout->b_die || p_vout->b_error )
371 msleep( VOUT_OUTMEM_SLEEP );
374 vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
376 for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
378 plane_t *p_in = p_pic->p+i_plane;
379 plane_t *p_out = p_outpic->p+i_plane;
380 int i_pitch = p_in->i_pitch;
383 for( i = 0; i < i_cols * i_rows; i++ )
385 int i_col = i % i_cols;
386 int i_row = i / i_cols;
387 int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
388 int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
389 int i_last_row = i_row + 1;
390 i_orow *= p_in->i_lines / i_rows;
391 i_row *= p_in->i_lines / i_rows;
392 i_last_row *= p_in->i_lines / i_rows;
394 if( p_vout->p_sys->b_blackslot == true
395 && p_vout->p_sys->b_finished == false
396 && i == p_vout->p_sys->i_selected )
398 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
399 for( ; i_row < i_last_row; i_row++, i_orow++ )
402 pf_memset( p_out->p_pixels + i_row * i_pitch
403 + i_col * i_pitch / i_cols,
404 color, i_pitch / i_cols );
409 for( ; i_row < i_last_row; i_row++, i_orow++ )
412 pf_memcpy( p_out->p_pixels + i_row * i_pitch
413 + i_col * i_pitch / i_cols,
414 p_in->p_pixels + i_orow * i_pitch
415 + i_ocol * i_pitch / i_cols,
422 if( p_vout->p_sys->i_selected != -1
423 && p_vout->p_sys->b_blackslot == false )
425 plane_t *p_in = p_pic->p+Y_PLANE;
426 plane_t *p_out = p_outpic->p+Y_PLANE;
427 int i_pitch = p_in->i_pitch;
428 int i_col = p_vout->p_sys->i_selected % i_cols;
429 int i_row = p_vout->p_sys->i_selected / i_cols;
430 int i_last_row = i_row + 1;
431 i_row *= p_in->i_lines / i_rows;
432 i_last_row *= p_in->i_lines / i_rows;
434 pf_memset( p_out->p_pixels + i_row * i_pitch
435 + i_col * i_pitch / i_cols,
436 0xff, i_pitch / i_cols );
437 for( ; i_row < i_last_row; i_row++ )
439 p_out->p_pixels[ i_row * i_pitch
440 + i_col * i_pitch / i_cols ] = 0xff;
441 p_out->p_pixels[ i_row * i_pitch
442 + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
446 pf_memset( p_out->p_pixels + i_row * i_pitch
447 + i_col * i_pitch / i_cols,
448 0xff, i_pitch / i_cols );
451 if( p_vout->p_sys->b_finished == true )
454 plane_t *p_out = p_outpic->p+Y_PLANE;
455 int i_pitch = p_out->i_pitch;
456 for( i = 0; i < SHUFFLE_HEIGHT; i++ )
458 for( j = 0; j < SHUFFLE_WIDTH; j++ )
460 if( shuffle_button[i][j] == '.' )
461 p_out->p_pixels[ i * i_pitch + j ] = 0xff;
466 vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
469 /*****************************************************************************
470 * SendEvents: forward mouse and keyboard events to the parent p_vout
471 *****************************************************************************/
472 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
473 vlc_value_t oldval, vlc_value_t newval, void *p_data )
475 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
477 var_Set( (vlc_object_t *)p_data, psz_var, newval );
482 /*****************************************************************************
483 * SendEventsToChild: forward events to the child/children vout
484 *****************************************************************************/
485 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
486 vlc_value_t oldval, vlc_value_t newval, void *p_data )
488 VLC_UNUSED(p_data); VLC_UNUSED(oldval);
489 vout_thread_t *p_vout = (vout_thread_t *)p_this;
490 var_Set( p_vout->p_sys->p_vout, psz_var, newval );
494 /*****************************************************************************
495 * MouseEvent: callback for mouse events
496 *****************************************************************************/
497 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
498 vlc_value_t oldval, vlc_value_t newval, void *p_data )
500 VLC_UNUSED(p_this); VLC_UNUSED(oldval); VLC_UNUSED(newval);
501 vout_thread_t *p_vout = (vout_thread_t*)p_data;
506 #define MOUSE_CLICKED 2
507 #define MOUSE_MOVE_X 4
508 #define MOUSE_MOVE_Y 8
509 #define MOUSE_MOVE 12
512 int v_h = p_vout->output.i_height;
513 int v_w = p_vout->output.i_width;
516 if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
517 if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
518 if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
520 i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
521 if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
522 i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
523 i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
525 if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
528 if( mouse & MOUSE_CLICKED )
530 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;
531 if( p_vout->p_sys->b_finished == true
532 && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
534 shuffle( p_vout->p_sys );
536 else if( p_vout->p_sys->i_selected == -1 )
538 p_vout->p_sys->i_selected = i_pos;
540 else if( p_vout->p_sys->i_selected == i_pos
541 && p_vout->p_sys->b_blackslot == false )
543 p_vout->p_sys->i_selected = -1;
545 else if( ( p_vout->p_sys->i_selected == i_pos + 1
546 && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
547 || ( p_vout->p_sys->i_selected == i_pos - 1
548 && i_pos % p_vout->p_sys->i_cols != 0 )
549 || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
550 || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
552 int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
553 p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
554 p_vout->p_sys->pi_order[ i_pos ];
555 p_vout->p_sys->pi_order[ i_pos ] = a;
556 if( p_vout->p_sys->b_blackslot == true )
557 p_vout->p_sys->i_selected = i_pos;
559 p_vout->p_sys->i_selected = -1;
561 p_vout->p_sys->b_finished = finished( p_vout->p_sys );
567 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
568 vlc_value_t oldval, vlc_value_t newval,
571 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
572 vout_sys_t *p_sys = (vout_sys_t *)p_data;
573 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
575 p_sys->i_rows = __MAX( 1, newval.i_int );
577 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
579 p_sys->i_cols = __MAX( 1, newval.i_int );
581 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
583 p_sys->b_blackslot = newval.b_bool;