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 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
35 #include "filter_common.h"
36 #include "vlc_image.h"
37 #include "vlc_input.h"
38 #include "vlc_playlist.h"
40 /*****************************************************************************
42 *****************************************************************************/
43 static int Create ( vlc_object_t * );
44 static void Destroy ( vlc_object_t * );
46 static int Init ( vout_thread_t * );
47 static void End ( vout_thread_t * );
48 static void Render ( vout_thread_t *, picture_t * );
50 static int SendEvents ( vlc_object_t *, char const *,
51 vlc_value_t, vlc_value_t, void * );
52 static int MouseEvent ( vlc_object_t *, char const *,
53 vlc_value_t, vlc_value_t, void * );
55 static int PuzzleCallback( vlc_object_t *, char const *,
56 vlc_value_t, vlc_value_t, void * );
58 /*****************************************************************************
60 *****************************************************************************/
62 #define ROWS_TEXT N_("Number of puzzle rows")
63 #define ROWS_LONGTEXT N_("Number of puzzle rows")
64 #define COLS_TEXT N_("Number of puzzle columns")
65 #define COLS_LONGTEXT N_("Number of puzzle columns")
66 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
67 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
69 #define CFG_PREFIX "puzzle-"
72 set_description( _("Puzzle interactive game video filter") );
73 set_shortname( _( "Puzzle" ));
74 set_capability( "video filter", 0 );
75 set_category( CAT_VIDEO );
76 set_subcategory( SUBCAT_VIDEO_VFILTER );
78 add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
79 ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE );
80 add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
81 COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
82 add_bool( CFG_PREFIX "black-slot", 0, NULL,
83 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_FALSE );
85 set_callbacks( Create, Destroy );
88 static const char *ppsz_filter_options[] = {
89 "rows", "cols", "black-slot", NULL
92 /*****************************************************************************
93 * vout_sys_t: Magnify video output method descriptor
94 *****************************************************************************/
97 vout_thread_t *p_vout;
99 image_handler_t *p_image;
105 vlc_bool_t b_finished;
107 vlc_bool_t b_blackslot;
110 /*****************************************************************************
111 * Control: control facility for the vout (forwards to child vout)
112 *****************************************************************************/
113 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
115 return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
118 /*****************************************************************************
120 *****************************************************************************/
121 static vlc_bool_t finished( vout_sys_t *p_sys )
124 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
126 if( i != p_sys->pi_order[i] ) return VLC_FALSE;
130 static vlc_bool_t is_valid( vout_sys_t *p_sys )
133 if( p_sys->b_blackslot == VLC_FALSE ) return VLC_TRUE;
134 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
136 if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
138 d += i / p_sys->i_cols + 1;
141 for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
143 if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
145 if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
148 if( d%2!=0 ) return VLC_FALSE;
149 else return VLC_TRUE;
151 static void shuffle( vout_sys_t *p_sys )
154 free( p_sys->pi_order );
155 p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
158 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
160 p_sys->pi_order[i] = -1;
163 for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
165 i = rand()%( p_sys->i_cols * p_sys->i_rows );
166 if( p_sys->pi_order[i] == -1 )
168 p_sys->pi_order[i] = c;
172 p_sys->b_finished = finished( p_sys );
173 } while( p_sys->b_finished == VLC_TRUE
174 || is_valid( p_sys ) == VLC_FALSE );
176 if( p_sys->b_blackslot == VLC_TRUE )
178 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
180 if( p_sys->pi_order[i] ==
181 p_sys->i_cols * p_sys->i_rows - 1 )
183 p_sys->i_selected = i;
190 p_sys->i_selected = -1;
194 /*****************************************************************************
195 * Create: allocates Magnify video thread output method
196 *****************************************************************************/
197 static int Create( vlc_object_t *p_this )
199 vout_thread_t *p_vout = (vout_thread_t *)p_this;
201 /* Allocate structure */
202 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
203 if( p_vout->p_sys == NULL )
205 msg_Err( p_vout, "out of memory" );
209 p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
211 config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
214 p_vout->p_sys->i_rows =
215 var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "rows" );
216 p_vout->p_sys->i_cols =
217 var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "cols" );
218 p_vout->p_sys->b_blackslot =
219 var_CreateGetBoolCommand( p_vout, CFG_PREFIX "black-slot" );
220 var_AddCallback( p_vout, CFG_PREFIX "rows",
221 PuzzleCallback, p_vout->p_sys );
222 var_AddCallback( p_vout, CFG_PREFIX "cols",
223 PuzzleCallback, p_vout->p_sys );
224 var_AddCallback( p_vout, CFG_PREFIX "black-slot",
225 PuzzleCallback, p_vout->p_sys );
227 p_vout->p_sys->pi_order = NULL;
228 shuffle( p_vout->p_sys );
230 p_vout->pf_init = Init;
231 p_vout->pf_end = End;
232 p_vout->pf_manage = NULL;
233 p_vout->pf_render = Render;
234 p_vout->pf_display = NULL;
235 p_vout->pf_control = Control;
240 /*****************************************************************************
241 * Init: initialize Magnify video thread output method
242 *****************************************************************************/
243 static int Init( vout_thread_t *p_vout )
248 memset( &fmt, 0, sizeof( video_format_t ) );
250 I_OUTPUTPICTURES = 0;
252 /* Initialize the output structure */
253 p_vout->output.i_chroma = p_vout->render.i_chroma;
254 p_vout->output.i_width = p_vout->render.i_width;
255 p_vout->output.i_height = p_vout->render.i_height;
256 p_vout->output.i_aspect = p_vout->render.i_aspect;
258 p_vout->fmt_out = p_vout->fmt_in;
259 fmt = p_vout->fmt_out;
261 /* Try to open the real video output */
262 msg_Dbg( p_vout, "spawning the real video output" );
264 p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
266 /* Everything failed */
267 if( p_vout->p_sys->p_vout == NULL )
269 msg_Err( p_vout, "cannot open vout, aborting" );
273 var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
274 var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
275 var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
278 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
279 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
280 ADD_PARENT_CALLBACKS( SendEventsToChild );
285 /*****************************************************************************
286 * End: terminate Magnify video thread output method
287 *****************************************************************************/
288 static void End( vout_thread_t *p_vout )
292 /* Free the fake output buffers we allocated */
293 for( i_index = I_OUTPUTPICTURES ; i_index ; )
296 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
299 var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
300 var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
301 var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
304 #define SHUFFLE_WIDTH 81
305 #define SHUFFLE_HEIGHT 13
306 static const char *shuffle_button[] =
308 ".................................................................................",
309 ".............. ............................ ........ ...... ...............",
310 ".............. ........................... ......... ........ ...............",
311 ".............. ........................... ......... ........ ...............",
312 ".. ....... . ....... .... ...... ...... ...... ........ ...",
313 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
314 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
315 ". ....... .... ...... .... ....... ......... ........ ...... .",
316 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
317 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
318 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
319 ".. ....... .... ....... . ....... ......... ........ ........ ..",
320 "................................................................................."};
323 /*****************************************************************************
324 * Destroy: destroy Magnify video thread output method
325 *****************************************************************************/
326 static void Destroy( vlc_object_t *p_this )
328 vout_thread_t *p_vout = (vout_thread_t *)p_this;
330 if( p_vout->p_sys->p_vout )
332 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
333 vlc_object_detach( p_vout->p_sys->p_vout );
334 vout_Destroy( p_vout->p_sys->p_vout );
337 image_HandlerDelete( p_vout->p_sys->p_image );
338 free( p_vout->p_sys->pi_order );
340 DEL_PARENT_CALLBACKS( SendEventsToChild );
342 free( p_vout->p_sys );
345 /*****************************************************************************
346 * Render: displays previously rendered output
347 *****************************************************************************/
348 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
352 //video_format_t fmt_out = {0};
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;