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 *****************************************************************************/
33 #include "filter_common.h"
34 #include "vlc_image.h"
35 #include "vlc_input.h"
36 #include "vlc_playlist.h"
38 /*****************************************************************************
40 *****************************************************************************/
41 static int Create ( vlc_object_t * );
42 static void Destroy ( vlc_object_t * );
44 static int Init ( vout_thread_t * );
45 static void End ( vout_thread_t * );
46 static void Render ( vout_thread_t *, picture_t * );
48 static int SendEvents ( vlc_object_t *, char const *,
49 vlc_value_t, vlc_value_t, void * );
50 static int MouseEvent ( vlc_object_t *, char const *,
51 vlc_value_t, vlc_value_t, void * );
53 static int PuzzleCallback( vlc_object_t *, char const *,
54 vlc_value_t, vlc_value_t, void * );
56 /*****************************************************************************
58 *****************************************************************************/
60 #define ROWS_TEXT N_("Number of puzzle rows")
61 #define ROWS_LONGTEXT N_("Number of puzzle rows")
62 #define COLS_TEXT N_("Number of puzzle columns")
63 #define COLS_LONGTEXT N_("Number of puzzle columns")
64 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
65 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
67 #define CFG_PREFIX "puzzle-"
70 set_description( _("Puzzle interactive game video filter") );
71 set_shortname( _( "Puzzle" ));
72 set_capability( "video filter", 0 );
73 set_category( CAT_VIDEO );
74 set_subcategory( SUBCAT_VIDEO_VFILTER );
76 add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
77 ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE );
78 add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
79 COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
80 add_bool( CFG_PREFIX "black-slot", 0, NULL,
81 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_FALSE );
83 set_callbacks( Create, Destroy );
86 static const char *ppsz_filter_options[] = {
87 "rows", "cols", "black-slot", NULL
90 /*****************************************************************************
91 * vout_sys_t: Magnify video output method descriptor
92 *****************************************************************************/
95 vout_thread_t *p_vout;
97 image_handler_t *p_image;
103 vlc_bool_t b_finished;
105 vlc_bool_t b_blackslot;
108 /*****************************************************************************
109 * Control: control facility for the vout (forwards to child vout)
110 *****************************************************************************/
111 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
113 return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
116 /*****************************************************************************
118 *****************************************************************************/
119 static vlc_bool_t finished( vout_sys_t *p_sys )
122 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
124 if( i != p_sys->pi_order[i] ) return VLC_FALSE;
128 static vlc_bool_t is_valid( vout_sys_t *p_sys )
131 if( p_sys->b_blackslot == VLC_FALSE ) return VLC_TRUE;
132 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
134 if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
136 d += i / p_sys->i_cols + 1;
139 for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
141 if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
143 if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
146 if( d%2!=0 ) return VLC_FALSE;
147 else return VLC_TRUE;
149 static void shuffle( vout_sys_t *p_sys )
152 free( p_sys->pi_order );
153 p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
156 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
158 p_sys->pi_order[i] = -1;
161 for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
163 i = rand()%( p_sys->i_cols * p_sys->i_rows );
164 if( p_sys->pi_order[i] == -1 )
166 p_sys->pi_order[i] = c;
170 p_sys->b_finished = finished( p_sys );
171 } while( p_sys->b_finished == VLC_TRUE
172 || is_valid( p_sys ) == VLC_FALSE );
174 if( p_sys->b_blackslot == VLC_TRUE )
176 for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
178 if( p_sys->pi_order[i] ==
179 p_sys->i_cols * p_sys->i_rows - 1 )
181 p_sys->i_selected = i;
188 p_sys->i_selected = -1;
192 /*****************************************************************************
193 * Create: allocates Magnify video thread output method
194 *****************************************************************************/
195 static int Create( vlc_object_t *p_this )
197 vout_thread_t *p_vout = (vout_thread_t *)p_this;
199 /* Allocate structure */
200 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
201 if( p_vout->p_sys == NULL )
203 msg_Err( p_vout, "out of memory" );
207 p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
209 config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
212 p_vout->p_sys->i_rows =
213 var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "rows" );
214 p_vout->p_sys->i_cols =
215 var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "cols" );
216 p_vout->p_sys->b_blackslot =
217 var_CreateGetBoolCommand( p_vout, CFG_PREFIX "black-slot" );
218 var_AddCallback( p_vout, CFG_PREFIX "rows",
219 PuzzleCallback, p_vout->p_sys );
220 var_AddCallback( p_vout, CFG_PREFIX "cols",
221 PuzzleCallback, p_vout->p_sys );
222 var_AddCallback( p_vout, CFG_PREFIX "black-slot",
223 PuzzleCallback, p_vout->p_sys );
225 p_vout->p_sys->pi_order = NULL;
226 shuffle( p_vout->p_sys );
228 p_vout->pf_init = Init;
229 p_vout->pf_end = End;
230 p_vout->pf_manage = NULL;
231 p_vout->pf_render = Render;
232 p_vout->pf_display = NULL;
233 p_vout->pf_control = Control;
238 /*****************************************************************************
239 * Init: initialize Magnify video thread output method
240 *****************************************************************************/
241 static int Init( vout_thread_t *p_vout )
246 memset( &fmt, 0, sizeof( video_format_t ) );
248 I_OUTPUTPICTURES = 0;
250 /* Initialize the output structure */
251 p_vout->output.i_chroma = p_vout->render.i_chroma;
252 p_vout->output.i_width = p_vout->render.i_width;
253 p_vout->output.i_height = p_vout->render.i_height;
254 p_vout->output.i_aspect = p_vout->render.i_aspect;
256 p_vout->fmt_out = p_vout->fmt_in;
257 fmt = p_vout->fmt_out;
259 /* Try to open the real video output */
260 msg_Dbg( p_vout, "spawning the real video output" );
262 p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
264 /* Everything failed */
265 if( p_vout->p_sys->p_vout == NULL )
267 msg_Err( p_vout, "cannot open vout, aborting" );
271 var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
272 var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
273 var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
276 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
277 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
278 ADD_PARENT_CALLBACKS( SendEventsToChild );
283 /*****************************************************************************
284 * End: terminate Magnify video thread output method
285 *****************************************************************************/
286 static void End( vout_thread_t *p_vout )
290 /* Free the fake output buffers we allocated */
291 for( i_index = I_OUTPUTPICTURES ; i_index ; )
294 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
297 var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
298 var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
299 var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
302 #define SHUFFLE_WIDTH 81
303 #define SHUFFLE_HEIGHT 13
304 static const char *shuffle_button[] =
306 ".................................................................................",
307 ".............. ............................ ........ ...... ...............",
308 ".............. ........................... ......... ........ ...............",
309 ".............. ........................... ......... ........ ...............",
310 ".. ....... . ....... .... ...... ...... ...... ........ ...",
311 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
312 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
313 ". ....... .... ...... .... ....... ......... ........ ...... .",
314 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
315 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
316 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
317 ".. ....... .... ....... . ....... ......... ........ ........ ..",
318 "................................................................................."};
321 /*****************************************************************************
322 * Destroy: destroy Magnify video thread output method
323 *****************************************************************************/
324 static void Destroy( vlc_object_t *p_this )
326 vout_thread_t *p_vout = (vout_thread_t *)p_this;
328 if( p_vout->p_sys->p_vout )
330 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
331 vlc_object_detach( p_vout->p_sys->p_vout );
332 vout_Destroy( p_vout->p_sys->p_vout );
335 image_HandlerDelete( p_vout->p_sys->p_image );
336 free( p_vout->p_sys->pi_order );
338 DEL_PARENT_CALLBACKS( SendEventsToChild );
340 free( p_vout->p_sys );
343 /*****************************************************************************
344 * Render: displays previously rendered output
345 *****************************************************************************/
346 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
350 //video_format_t fmt_out;
351 // memset( &fmt_out, 0, sizeof(video_format_t) );
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;