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, VLC_FALSE );
82 add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
83 COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
84 add_bool( CFG_PREFIX "black-slot", 0, NULL,
85 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_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;
107 vlc_bool_t b_finished;
109 vlc_bool_t b_blackslot;
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 vlc_bool_t 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 VLC_FALSE;
132 static vlc_bool_t is_valid( vout_sys_t *p_sys )
135 if( p_sys->b_blackslot == VLC_FALSE ) return VLC_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 VLC_FALSE;
151 else return VLC_TRUE;
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 == VLC_TRUE
176 || is_valid( p_sys ) == VLC_FALSE );
178 if( p_sys->b_blackslot == VLC_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 == VLC_TRUE
395 && p_vout->p_sys->b_finished == VLC_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 == VLC_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 == VLC_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 var_Set( (vlc_object_t *)p_data, psz_var, newval );
480 /*****************************************************************************
481 * SendEventsToChild: forward events to the child/children vout
482 *****************************************************************************/
483 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
484 vlc_value_t oldval, vlc_value_t newval, void *p_data )
486 vout_thread_t *p_vout = (vout_thread_t *)p_this;
487 var_Set( p_vout->p_sys->p_vout, psz_var, newval );
491 /*****************************************************************************
492 * MouseEvent: callback for mouse events
493 *****************************************************************************/
494 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
495 vlc_value_t oldval, vlc_value_t newval, void *p_data )
497 vout_thread_t *p_vout = (vout_thread_t*)p_data;
502 #define MOUSE_CLICKED 2
503 #define MOUSE_MOVE_X 4
504 #define MOUSE_MOVE_Y 8
505 #define MOUSE_MOVE 12
508 int v_h = p_vout->output.i_height;
509 int v_w = p_vout->output.i_width;
512 if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
513 if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
514 if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
516 i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
517 if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
518 i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
519 i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
521 if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
524 if( mouse & MOUSE_CLICKED )
526 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;
527 if( p_vout->p_sys->b_finished == VLC_TRUE
528 && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
530 shuffle( p_vout->p_sys );
532 else if( p_vout->p_sys->i_selected == -1 )
534 p_vout->p_sys->i_selected = i_pos;
536 else if( p_vout->p_sys->i_selected == i_pos
537 && p_vout->p_sys->b_blackslot == VLC_FALSE )
539 p_vout->p_sys->i_selected = -1;
541 else if( ( p_vout->p_sys->i_selected == i_pos + 1
542 && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
543 || ( p_vout->p_sys->i_selected == i_pos - 1
544 && i_pos % p_vout->p_sys->i_cols != 0 )
545 || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
546 || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
548 int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
549 p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
550 p_vout->p_sys->pi_order[ i_pos ];
551 p_vout->p_sys->pi_order[ i_pos ] = a;
552 if( p_vout->p_sys->b_blackslot == VLC_TRUE )
553 p_vout->p_sys->i_selected = i_pos;
555 p_vout->p_sys->i_selected = -1;
557 p_vout->p_sys->b_finished = finished( p_vout->p_sys );
563 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
564 vlc_value_t oldval, vlc_value_t newval,
567 vout_sys_t *p_sys = (vout_sys_t *)p_data;
568 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
570 p_sys->i_rows = __MAX( 1, newval.i_int );
572 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
574 p_sys->i_cols = __MAX( 1, newval.i_int );
576 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
578 p_sys->b_blackslot = newval.b_bool;