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 )
247 video_format_t fmt = {0};
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 = {0};
352 //picture_t *p_converted;
356 int i_rows = p_vout->p_sys->i_rows;
357 int i_cols = p_vout->p_sys->i_cols;
359 /* This is a new frame. Get a structure from the video_output. */
360 while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
363 if( p_vout->b_die || p_vout->b_error )
367 msleep( VOUT_OUTMEM_SLEEP );
370 vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
372 for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
374 plane_t *p_in = p_pic->p+i_plane;
375 plane_t *p_out = p_outpic->p+i_plane;
376 int i_pitch = p_in->i_pitch;
379 for( i = 0; i < i_cols * i_rows; i++ )
381 int i_col = i % i_cols;
382 int i_row = i / i_cols;
383 int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
384 int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
385 int i_last_row = i_row + 1;
386 i_orow *= p_in->i_lines / i_rows;
387 i_row *= p_in->i_lines / i_rows;
388 i_last_row *= p_in->i_lines / i_rows;
390 if( p_vout->p_sys->b_blackslot == VLC_TRUE
391 && p_vout->p_sys->b_finished == VLC_FALSE
392 && i == p_vout->p_sys->i_selected )
394 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
395 for( ; i_row < i_last_row; i_row++, i_orow++ )
398 pf_memset( p_out->p_pixels + i_row * i_pitch
399 + i_col * i_pitch / i_cols,
400 color, i_pitch / i_cols );
405 for( ; i_row < i_last_row; i_row++, i_orow++ )
408 pf_memcpy( p_out->p_pixels + i_row * i_pitch
409 + i_col * i_pitch / i_cols,
410 p_in->p_pixels + i_orow * i_pitch
411 + i_ocol * i_pitch / i_cols,
418 if( p_vout->p_sys->i_selected != -1
419 && p_vout->p_sys->b_blackslot == VLC_FALSE )
421 plane_t *p_in = p_pic->p+Y_PLANE;
422 plane_t *p_out = p_outpic->p+Y_PLANE;
423 int i_pitch = p_in->i_pitch;
424 int i_col = p_vout->p_sys->i_selected % i_cols;
425 int i_row = p_vout->p_sys->i_selected / i_cols;
426 int i_last_row = i_row + 1;
427 i_row *= p_in->i_lines / i_rows;
428 i_last_row *= p_in->i_lines / i_rows;
430 pf_memset( p_out->p_pixels + i_row * i_pitch
431 + i_col * i_pitch / i_cols,
432 0xff, i_pitch / i_cols );
433 for( ; i_row < i_last_row; i_row++ )
435 p_out->p_pixels[ i_row * i_pitch
436 + i_col * i_pitch / i_cols ] = 0xff;
437 p_out->p_pixels[ i_row * i_pitch
438 + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
442 pf_memset( p_out->p_pixels + i_row * i_pitch
443 + i_col * i_pitch / i_cols,
444 0xff, i_pitch / i_cols );
447 if( p_vout->p_sys->b_finished == VLC_TRUE )
450 plane_t *p_out = p_outpic->p+Y_PLANE;
451 int i_pitch = p_out->i_pitch;
452 for( i = 0; i < SHUFFLE_HEIGHT; i++ )
454 for( j = 0; j < SHUFFLE_WIDTH; j++ )
456 if( shuffle_button[i][j] == '.' )
457 p_out->p_pixels[ i * i_pitch + j ] = 0xff;
462 vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
465 /*****************************************************************************
466 * SendEvents: forward mouse and keyboard events to the parent p_vout
467 *****************************************************************************/
468 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
469 vlc_value_t oldval, vlc_value_t newval, void *p_data )
471 var_Set( (vlc_object_t *)p_data, psz_var, newval );
476 /*****************************************************************************
477 * SendEventsToChild: forward events to the child/children vout
478 *****************************************************************************/
479 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
480 vlc_value_t oldval, vlc_value_t newval, void *p_data )
482 vout_thread_t *p_vout = (vout_thread_t *)p_this;
483 var_Set( p_vout->p_sys->p_vout, psz_var, newval );
487 /*****************************************************************************
488 * MouseEvent: callback for mouse events
489 *****************************************************************************/
490 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
491 vlc_value_t oldval, vlc_value_t newval, void *p_data )
493 vout_thread_t *p_vout = (vout_thread_t*)p_data;
498 #define MOUSE_CLICKED 2
499 #define MOUSE_MOVE_X 4
500 #define MOUSE_MOVE_Y 8
501 #define MOUSE_MOVE 12
504 int v_h = p_vout->output.i_height;
505 int v_w = p_vout->output.i_width;
508 if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
509 if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
510 if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
512 i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
513 if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
514 i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
515 i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
517 if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
520 if( mouse & MOUSE_CLICKED )
522 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;
523 if( p_vout->p_sys->b_finished == VLC_TRUE
524 && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
526 shuffle( p_vout->p_sys );
528 else if( p_vout->p_sys->i_selected == -1 )
530 p_vout->p_sys->i_selected = i_pos;
532 else if( p_vout->p_sys->i_selected == i_pos
533 && p_vout->p_sys->b_blackslot == VLC_FALSE )
535 p_vout->p_sys->i_selected = -1;
537 else if( ( p_vout->p_sys->i_selected == i_pos + 1
538 && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
539 || ( p_vout->p_sys->i_selected == i_pos - 1
540 && i_pos % p_vout->p_sys->i_cols != 0 )
541 || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
542 || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
544 int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
545 p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
546 p_vout->p_sys->pi_order[ i_pos ];
547 p_vout->p_sys->pi_order[ i_pos ] = a;
548 if( p_vout->p_sys->b_blackslot == VLC_TRUE )
549 p_vout->p_sys->i_selected = i_pos;
551 p_vout->p_sys->i_selected = -1;
553 p_vout->p_sys->b_finished = finished( p_vout->p_sys );
559 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
560 vlc_value_t oldval, vlc_value_t newval,
563 vout_sys_t *p_sys = (vout_sys_t *)p_data;
564 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
566 p_sys->i_rows = __MAX( 1, newval.i_int );
568 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
570 p_sys->i_cols = __MAX( 1, newval.i_int );
572 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
574 p_sys->b_blackslot = newval.b_bool;